本文共 4140 字,大约阅读时间需要 13 分钟。
转自:http://linux.chinaunix.net/techdoc/develop/2008/03/07/982180.shtml
摘要 Linux内核模块编程的资料有些纷繁复杂,有的过于简单,有的过于庞杂,我试图用笔记的形式想读者展示怎样来进程Linux模块编程,力图做到简 明扼要,这篇文章也是作为本人备忘的资料,所以有些地方过于简略是难免的。本来这篇文章的目的就是让用户知其然,至于所以然还是请参考相应的资料,其实最 好的资料莫过于Linux Kernel Source。 适用范围:
Linux模块简介 首先这个module不同于microkernel的module,microkernel的module是一个个的daemon进程,工作于用户 空间,Linux的module只是一个内核的目标代码,内核通过执行运行时的连接,来把它整合到kernel中去,所以说Linux的module机制 并没有改变Linux内核为monolithic OS本质,其module也是工作于内核模式,享有内核的所有特权。 至于为什么要引入Linux Kernle Module(一下简称LKM),我想至少有一下几点:
相关宏及头文件 LKM需要包含以下头文件: 需要定义以下宏:__KERNEL__, MODULE 一个简单的内核模块示例/*file: hello.c*/ #ifndef __KERNEL__ #define __KERNEL__ #endif #ifndef MODULE #define MODULE #endif #include #include static int __init hello_init(void) { printk(KERN_ALERT "Hello, my LKM./n"); return 0; } static void __exit hello_exit(void) { printk(KERN_ALERT "Bye, my LKM./n"); } module_init(hello_init); module_exit(hello_exit); 很简答吧,不是吗?这个LKM的功能其实也很简单,就是当通过insmod加载它的时候,他打印Hello, my LKM.通过rmmod卸载它的时候他打印bye, my LKM.一个最基本的内核模块一般都包含有两个函数,一个是初始化函数(比如说这里的hello_init),一个是卸载函数(hello_exit), 当然也可以没有任何函数,只是提供一些变量。但是初始化函数和卸载函数必须成对出现。并且init函数当操作成功时返回值大于等于零,当操作失败时,返回 非零。宏module_init和module_exit用于注册初始化函数和卸载函数。 LKM的编译 一个示例的Makefile如下所示 obj-m := hello.o KERNELBUILD := /lib/modules/`uname -r`/build default: make -C $(KERNELBUILD) M=$(shell pwd) modules clean: rm -rf *.o .*.cmd *.ko *.mod.c .tmp_versions 如果这个目录下面还有其它模块,只需要在hello.o后面添加就行了。 obj-m := hello.o mod.o 在模块所在目录执行make,等成功后就可以得到我们想要的模块(hello.ko)了。 如果一个模块存在许多源文件,比如:hello, 由hello1.c hello2.c共同连接而成,需要在Makefile中加入如下行 hello-objs := hello1.o hello2.o LKM的加载 Linux为用户提供了modutils,用来操纵模块。这个工具集主要包括: insmod 安装模块 rmmod 删除模块 modprobe 比较高级的加载和删除模块,可以解决模块之间的依赖性 lsmod 列出已经加载的模块和其信息 modinfo 用于查询模块的相关信息,比如作者,版权... 试着用命令insmod hello.ko加载模块,rmmod删除模块,看看有什么事情发生了。你有可能看不见任何输出,难道是有错误发生?No,执行命令tail /var/log/message呵呵,是不是看到了? Feb 19 00:07:35 gentux Hello, my LKM. Feb 19 00:07:38 gentux Bye, my LKM. 模块其它信息 比较常用信息常常包括:作者、描述、版权等,为此LKM为我们提供了如下宏: MODULE_AUTHOR("author"); MODULE_DESCRIPTION("the description"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("dev"); // 设备驱动程序所支持的设备。 比较常用的Free license有"GPL", "GPL v2", "GPL and additional rights", "Dual BSD/GPL", "Dual MPL/GPL"。 模块参数 用户空间的应用程序可以接受用户的参数,LKM也可以做到,只是方式有些不同而已。相关的宏有: MODULE_PARM(var, type); MODULE_PARM_DESC(var, "the description of the var"); 模块参数的类型(即MODULE_PARM中的type)有一下几种:
这些参数最好有默认值,如果有些必要参数用户没有设置可以通过在module_init指定的init函数返回负值来拒绝模块的加载。 LKM还支持数组类型的模块,如果在类型符号前加上数字n则表示最大程度为n的数组,用“-”隔开的数字分别代表最小和最大的数组长度。 示例: MODULE_PARM(var, "4i"); // 最大长度为4的整形数组 MODULE_PARM(var, "2-6i"); // 最小长度为2,最大长度为6的整形数组 如何用insmod传入参数,其实man一下就可以了,不过现在的man有些过于简单,所以在此说明一下: insmod variable=value[,value2...] ... 其中value可以用引号括起来,也可以不用。但是有一点“=”前后不能留有空格,并且value中也不能有空格。 模块符号的导出 和用户空间的应用程序不同的是,引入一个模块的目的常常是为了给内核提供一些routine,来完成特定的功能,很少有模块什么符号都不导出,为此Linux为用户提供了如下宏: EXPORT_SYMBOL(var); // 输出symbol var EXPORT_SYMBOL_GPL(var); // 输出的symbol版权为GPL 模块之间的依赖性 有的时候两个模块之间可能有依赖性,要加载的模块A,依赖于模块B,此时insmod是无能为力的,只能用modprobe来加载模块和其依赖的模块,否则只能手动一个个加载。 modprobe通过读取由depmod -a生成的/lib/modules/version/modules.dep来获得其所依赖的模块列表(也有可能是一个模块树),然后调用insmod来一个个按顺序加载。 命名空间的问题
|
转载地址:http://cbemb.baihongyu.com/