Difference between revisions of "OS: Membuat Kernel Modul"

From OnnoWiki
Jump to navigation Jump to search
Line 9: Line 9:
 
For this lesson, your kernel must have been compiled with these options:
 
For this lesson, your kernel must have been compiled with these options:
  
Loadable module support  --->
+
Loadable module support  --->
  [*] Enable loadable module support
+
  [*] Enable loadable module support
  [*]  Module unloading
+
  [*]  Module unloading
  [ ]  Module versioning support (EXPERIMENTAL)
+
  [ ]  Module versioning support (EXPERIMENTAL)
  [*]  Automatic kernel module loading     
+
  [*]  Automatic kernel module loading     
  
 
If you compiled your kernel according to the instructions in the first few kernel lessons, you should already have these options properly set. Otherwise, change these options, recompile the kernel, and boot into your new kernel.
 
If you compiled your kernel according to the instructions in the first few kernel lessons, you should already have these options properly set. Otherwise, change these options, recompile the kernel, and boot into your new kernel.
A simple module skeleton
+
 
 +
==A simple module skeleton==
  
 
First, find the source that your current Linux kernel was compiled from. Change directory to drivers/misc/ in your Linux source code directory. Now, copy and paste the following code into a file named mymodule.c:
 
First, find the source that your current Linux kernel was compiled from. Change directory to drivers/misc/ in your Linux source code directory. Now, copy and paste the following code into a file named mymodule.c:
  
#include <linux/module.h>
+
#include <linux/module.h>
#include <linux/config.h>
+
#include <linux/config.h>
#include <linux/init.h>
+
#include <linux/init.h>
 
+
static int __init mymodule_init(void)
+
static int __init mymodule_init(void)
{
+
{
printk ("My module worked!\n");
+
  printk ("My module worked!\n");
        return 0;
+
        return 0;
}
+
}
 
+
static void __exit mymodule_exit(void)
+
static void __exit mymodule_exit(void)
{
+
{
printk ("Unloading my module.\n");
+
  printk ("Unloading my module.\n");
        return;
+
        return;
}
+
}
 
+
module_init(mymodule_init);
+
module_init(mymodule_init);
module_exit(mymodule_exit);
+
module_exit(mymodule_exit);
 
+
MODULE_LICENSE("GPL");
+
MODULE_LICENSE("GPL");
  
 
Save the file and edit the Makefile in the same directory. Add this line:
 
Save the file and edit the Makefile in the same directory. Add this line:
  
obj-m += mymodule.o
+
obj-m += mymodule.o
  
 
Compile your module:
 
Compile your module:
  
  # make -C [top directory of your kernel source] SUBDIRS=$PWD modules
+
# make -C [top directory of your kernel source] SUBDIRS=$PWD modules
  
 
Load the module. Depending on your kernel version, do that with either:
 
Load the module. Depending on your kernel version, do that with either:
  
  # insmod ./mymodule.o
+
# insmod ./mymodule.o
  
 
Or:
 
Or:
  
  # insmod ./mymodule.ko
+
# insmod ./mymodule.ko
  
 
And check to see if your message printed out:
 
And check to see if your message printed out:
  
  # dmesg | tail
+
# dmesg | tail
  
 
You should see this at the end of the output:
 
You should see this at the end of the output:
  
  My module worked!
+
My module worked!
  
 
Now remove the kernel module:
 
Now remove the kernel module:
  
  # rmmod mymodule
+
# rmmod mymodule
  
 
Check the output of dmesg again, you should see:
 
Check the output of dmesg again, you should see:
  
  Unloading my module.
+
Unloading my module.
  
 
You just wrote and ran a new kernel module! Congratulations!
 
You just wrote and ran a new kernel module! Congratulations!
Line 80: Line 81:
 
Edit the file kernel/printk.c and add this line after all the included files and near the other global variable declarations (but outside all functions):
 
Edit the file kernel/printk.c and add this line after all the included files and near the other global variable declarations (but outside all functions):
  
  int my_variable = 0;
+
int my_variable = 0;
  
 
Now recompile your kernel and reboot into your new kernel. Next, add this to the beginning of your module's mymodule_init function, before the other code:
 
Now recompile your kernel and reboot into your new kernel. Next, add this to the beginning of your module's mymodule_init function, before the other code:
  
  extern int my_variable;
+
extern int my_variable;
  printk ("my_variable is %d\n", my_variable);
+
printk ("my_variable is %d\n", my_variable);
  my_variable++;
+
my_variable++;
  
 
Save your changes and recompile your module:
 
Save your changes and recompile your module:
  
  # make -C path/to/kernel/src SUBDIRS=$PWD modules
+
# make -C path/to/kernel/src SUBDIRS=$PWD modules
  
 
And load the module (this will fail):
 
And load the module (this will fail):
  
  # insmod ./mymodule.ko
+
# insmod ./mymodule.ko
  
 
Loading your module should fail with the message:
 
Loading your module should fail with the message:
  
  insmod: error inserting './mymodule.ko': -1 Unknown symbol in module
+
insmod: error inserting './mymodule.ko': -1 Unknown symbol in module
  
 
What this is saying is that the kernel is not allowing modules to see that variable. When the module loads, it has to resolve all it's external references, like function names or variable names. If it can't find all of it's unresolved names in the list of symbols that the kernel exports, then the module can't write to that variable or call that function. The variable my_variable has space allocated for it somewhere in the kernel, but the module can't figure out where.
 
What this is saying is that the kernel is not allowing modules to see that variable. When the module loads, it has to resolve all it's external references, like function names or variable names. If it can't find all of it's unresolved names in the list of symbols that the kernel exports, then the module can't write to that variable or call that function. The variable my_variable has space allocated for it somewhere in the kernel, but the module can't figure out where.
Line 104: Line 105:
 
To fix this, we're going to add my_variable to the list of symbols that the kernel exports. Many kernel directories have a file specifically for exporting symbols defined in that directory. Bring up the file kernel/printk.c again and add this line after the declaration of your variable:
 
To fix this, we're going to add my_variable to the list of symbols that the kernel exports. Many kernel directories have a file specifically for exporting symbols defined in that directory. Bring up the file kernel/printk.c again and add this line after the declaration of your variable:
  
  EXPORT_SYMBOL_NOVERS(my_variable);
+
EXPORT_SYMBOL_NOVERS(my_variable);
  
 
Recompile and reboot into your new kernel. Now try to load your module again:
 
Recompile and reboot into your new kernel. Now try to load your module again:
  
  # insmod ./mymodule.ko
+
# insmod ./mymodule.ko
  
 
This time, when you check dmesg, you should see:
 
This time, when you check dmesg, you should see:
  
  my_variable is 0
+
my_variable is 0
  My module worked!
+
My module worked!
  
 
Reload your module:
 
Reload your module:
  
  # rmmod mymodule && insmod ./mymodule.ko
+
# rmmod mymodule && insmod ./mymodule.ko
  
 
Now you should see:
 
Now you should see:
  
  Unloading my module.
+
Unloading my module.
  my_variable is 1     
+
my_variable is 1     
  My module worked!
+
My module worked!
  
 
Each time you reload the module, my_variable should increase by one. You are reading and writing to a variable which is defined in the main kernel. Your module can access any variable or function in the main kernel, as long as it is explicitly exported via the EXPORT_SYMBOL() declaration. For example, the function printk() is defined in the kernel and exported in the file kernel/printk.c.
 
Each time you reload the module, my_variable should increase by one. You are reading and writing to a variable which is defined in the main kernel. Your module can access any variable or function in the main kernel, as long as it is explicitly exported via the EXPORT_SYMBOL() declaration. For example, the function printk() is defined in the kernel and exported in the file kernel/printk.c.
Line 129: Line 130:
 
A simple loadable kernel module is a fun way to explore the kernel. For example, you can use a module to turn a printk on or off, by defining a variable do_print in the kernel which is initially set to 0. Then make all your printk's dependent on "do_print":
 
A simple loadable kernel module is a fun way to explore the kernel. For example, you can use a module to turn a printk on or off, by defining a variable do_print in the kernel which is initially set to 0. Then make all your printk's dependent on "do_print":
  
  if (do_print)
+
if (do_print)
    printk ("Big long obnoxious message\n");
+
  printk ("Big long obnoxious message\n");
  
 
And turn on do_print only when your module is loaded. You can add a function defined in your module to the list of functions that are called when the kernel receives a certain interrupt (use cat /proc/interrupts to find out what interrupts are in use). The function request_irq() adds your function to the list of handlers for a selected irq line, which you can use to print out a message each time you receive an interrupt on that line. You can investigate the current value of any exported variable by loading a module that reads that value and immediately exits (returns a non-zero value from the module_init() function). The variable jiffies, which increments every 1/100th of a second (on most platforms), is a good candidate for this kind of module.
 
And turn on do_print only when your module is loaded. You can add a function defined in your module to the list of functions that are called when the kernel receives a certain interrupt (use cat /proc/interrupts to find out what interrupts are in use). The function request_irq() adds your function to the list of handlers for a selected irq line, which you can use to print out a message each time you receive an interrupt on that line. You can investigate the current value of any exported variable by loading a module that reads that value and immediately exits (returns a non-zero value from the module_init() function). The variable jiffies, which increments every 1/100th of a second (on most platforms), is a good candidate for this kind of module.

Revision as of 11:10, 2 April 2013

Sumber:


In this lesson, we'll write and load a simple kernel module. Writing your own module lets you write some standalone kernel code, learn how to use modules, and discover a few rules about how the kernel links together. Note: These instructions were written for the 2.6.x kernels and may not work with different kernel versions. Does your kernel support modules?

For this lesson, your kernel must have been compiled with these options:

Loadable module support  --->
  [*] Enable loadable module support
  [*]   Module unloading
  [ ]   Module versioning support (EXPERIMENTAL)
  [*]   Automatic kernel module loading    

If you compiled your kernel according to the instructions in the first few kernel lessons, you should already have these options properly set. Otherwise, change these options, recompile the kernel, and boot into your new kernel.

A simple module skeleton

First, find the source that your current Linux kernel was compiled from. Change directory to drivers/misc/ in your Linux source code directory. Now, copy and paste the following code into a file named mymodule.c:

#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>

static int __init mymodule_init(void)
{
 printk ("My module worked!\n");
        return 0;
}

static void __exit mymodule_exit(void)
{
 printk ("Unloading my module.\n");
        return;
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");

Save the file and edit the Makefile in the same directory. Add this line:

obj-m += mymodule.o

Compile your module:

# make -C [top directory of your kernel source] SUBDIRS=$PWD modules

Load the module. Depending on your kernel version, do that with either:

# insmod ./mymodule.o

Or:

# insmod ./mymodule.ko

And check to see if your message printed out:

# dmesg | tail

You should see this at the end of the output:

My module worked!

Now remove the kernel module:

# rmmod mymodule

Check the output of dmesg again, you should see:

Unloading my module.

You just wrote and ran a new kernel module! Congratulations! The module/kernel interface

Now, let's do some more interesting things with your module. One of the key things to realize is that modules can only "see" functions and variables that the kernel deliberately makes visible to the modules. First, let's try to do things the wrong way.

Edit the file kernel/printk.c and add this line after all the included files and near the other global variable declarations (but outside all functions):

int my_variable = 0;

Now recompile your kernel and reboot into your new kernel. Next, add this to the beginning of your module's mymodule_init function, before the other code:

extern int my_variable;
printk ("my_variable is %d\n", my_variable);
my_variable++;

Save your changes and recompile your module:

# make -C path/to/kernel/src SUBDIRS=$PWD modules

And load the module (this will fail):

# insmod ./mymodule.ko

Loading your module should fail with the message:

insmod: error inserting './mymodule.ko': -1 Unknown symbol in module

What this is saying is that the kernel is not allowing modules to see that variable. When the module loads, it has to resolve all it's external references, like function names or variable names. If it can't find all of it's unresolved names in the list of symbols that the kernel exports, then the module can't write to that variable or call that function. The variable my_variable has space allocated for it somewhere in the kernel, but the module can't figure out where.

To fix this, we're going to add my_variable to the list of symbols that the kernel exports. Many kernel directories have a file specifically for exporting symbols defined in that directory. Bring up the file kernel/printk.c again and add this line after the declaration of your variable:

EXPORT_SYMBOL_NOVERS(my_variable);

Recompile and reboot into your new kernel. Now try to load your module again:

# insmod ./mymodule.ko

This time, when you check dmesg, you should see:

my_variable is 0
My module worked!

Reload your module:

# rmmod mymodule && insmod ./mymodule.ko

Now you should see:

Unloading my module.
my_variable is 1    
My module worked!

Each time you reload the module, my_variable should increase by one. You are reading and writing to a variable which is defined in the main kernel. Your module can access any variable or function in the main kernel, as long as it is explicitly exported via the EXPORT_SYMBOL() declaration. For example, the function printk() is defined in the kernel and exported in the file kernel/printk.c.

A simple loadable kernel module is a fun way to explore the kernel. For example, you can use a module to turn a printk on or off, by defining a variable do_print in the kernel which is initially set to 0. Then make all your printk's dependent on "do_print":

if (do_print)
  printk ("Big long obnoxious message\n");

And turn on do_print only when your module is loaded. You can add a function defined in your module to the list of functions that are called when the kernel receives a certain interrupt (use cat /proc/interrupts to find out what interrupts are in use). The function request_irq() adds your function to the list of handlers for a selected irq line, which you can use to print out a message each time you receive an interrupt on that line. You can investigate the current value of any exported variable by loading a module that reads that value and immediately exits (returns a non-zero value from the module_init() function). The variable jiffies, which increments every 1/100th of a second (on most platforms), is a good candidate for this kind of module.

Play with your new kernel module - modules are fun!



Referensi

Pranala Menarik