Linux提供了这样一种机制,这种机制被称为模块。模块具有这样的特点:
模块本身不被编译入内核映像,从而控制了内核的大小。
模块一旦被加载,它就和内核中的其它部分完全一样。
一、模块的组成
一个Linux内核模块主要由如下几个部分组成:
(1)模块加载函数
Linux 内核模块加载函数一般以__init标识声明。
内核模块加载函数:
static int __init initialization_function(void)
{
/*初始化代码*/
}
module_init(initialization_function);
模块加载函数必需以“module_init(函数名)”的形式被指定。它返回整型值,若初始化成功应返回0.而在初始化失败时,应该返回错误编码。在Linux内核里错误编码应该是一个负值,在<linux/errno.h>中定义,包含-ENODEV,
-ENOMEM之类的符号值。
(2)模块卸载函数
Linux内核模块卸载函数一般以__exit标识声明,典型的模块的卸载函数的形式如下所示:
static void __exit cleanup_function(void)
{
/*释放代码*/
}
module_exit(cleanup_function);
模块卸载函数在模块卸载的时候执行,不返回任何值,必须以"module_exit(函数名)"的形式来指定。
(3)模块许可证声明
MODULE_LICENSE("GPL");
(4)模块参数
我们可以用“module_param(参数名, 参数类型, 参数读/写权限)”为模块定义一个参数。例如下列代码定义了一个整型参数和一个字符指针参数:
static char *book_name = "dissectting Linux Device Driver";
static int num = 4000;
module_param(num, int, S_IRUGO);
module_param(book_name, charp, S_IRUGO);
实例:带参数的内核模块
/*start*/
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static char *book_name = "dissectting Linux Device Driver";
static int num = 4000;
static int __init book_init(void)
{
printk(KERN_ALERT " book name: %s\n", book_name);
printk(KERN_ALERT " book num: %d\n", num);
return 0;
}
static void __exit book_exit(void)
{
printk(KERN_ALERT " Book module eixt\n");
}
module_init(book_init);
module_exit(book_exit);
module_param(num, int, S_IRUGO);
module_param(book_name, charp, S_IRUGO);
/*end*/
编译和运行:
模块加载:
insmod book.ko book_name=‘GoodBook‘ num=250
Message from syslogd@localhost at Mar 26 19:52:24 ...
kernel: book name: GoodBook
Message from syslogd@localhost at Mar 26 19:52:24 ...
kernel: book num: 250
模块卸载:
rmmod book
Message from syslogd@localhost at Mar 26 19:54:14 ...
kernel: Book module eixt
(5)模块导出符号
Linux2.6的“/proc/kallsyms” 文件对应着内核符号表,它记录了符号及符号所在的内存地址。
模块可以使用如下宏导出符号到内核符号表:
EXPORT_SYMBOL(符号名);
EXPORT_SYMBOL_GPL(符号名);
导出的符号可以被其它模块使用,使用前声明一下即可。EXPORT_SYMBOL_GPL()只适用于包含GPL许可权的模块。
实例:内核模块中的符号导出
/*start*/
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
int add_integar(int a, int b)
{
return a + b;
}
int sub_integar(int a, int b)
{
return a - b;
}
EXPORT_SYMBOL(add_integar);
EXPORT_SYMBOL(sub_integar);
/*end*/
insmod symbol.ko
cat /proc/callsyms | grep integar
f7e42038 r __ksymtab_sub_integar[symbol]
f7e42050 r __kstrtab_sub_integar[symbol]
f7e42048 r __kcrctab_sub_integar[symbol]
f7e42040 r __ksymtab_add_integar[symbol]
f7e4205c r __kstrtab_add_integar[symbol]
f7e4204c r __kcrctab_add_integar[symbol]
f7e42000 T add_integar[symbol]
f7e42010 T sub_integar[symbol]
rmmod symbol
(6)模块作者等信息声明
在linux内核模块中,我们可以用 MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS分别声明模块的作者、描述、版本、设备表和别名。
对于USB、PCI等设备驱动,通常会创建一个MODULE_DEVICE_TABLE,表明该驱动模块所支持的设备:
static struct usb_device_id skel_table[] =
{
USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID),
{ }/*结束*/
};
二、模块的编译
一个简单的Makefile:
/*$$*/
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /lib/modules/2.6.32-358.el6.i686/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.ko.unsigned *.order *.symvers
endif
/*$$*/