Linux 内核模块简介
2014-03-27 11:45
387 查看
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);
实例:带参数的内核模块
编译和运行:
模块加载:
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许可权的模块。
实例:内核模块中的符号导出
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:
模块本身不被编译入内核映像,从而控制了内核的大小。
模块一旦被加载,它就和内核中的其它部分完全一样。
一、模块的组成
一个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 /*$$*/
相关文章推荐
- Linux 死锁检测模块 Lockdep 简介——转自魅族内核团队,对死锁检测认识上升到新高度
- linux驱动学习--第五天:第四章 Linux 内核模块 之 Linux 内核模块简介
- linux 内核模块简介
- Linux 内核模块简介及常用命令
- 【Linux开发】内核模块简介
- 内核模块简介(1)-linux内核体系结构(1)
- 转:linux启动时对编译进内核驱动模块的加载
- Linux 最简单内核模块 Hello World 示例
- Linux下hello.ko内核模块制作的全过程
- Linux 2.6.19.x 内核编译配置选项简介
- Linux 2.6内核的编译步骤及模块的动态加载
- Linux内核2.6简介
- Linux 2.6版内核中通过模块获取sys_call_table地址的方法
- Linux 2.6内核的编译步骤及模块的动态加载
- Linux 内核引导选项简介
- Linux 2.6内核下编译可以加载的内核模块
- Linux 2.6.19.x 内核编译配置选项简介
- Linux 2.6.19.x 内核编译配置选项简介
- Linux 2.6.19.x 内核编译配置选项简介
- Linux 内核模块编译(hello.c)