学习Linux内核模块编写总结

在学习编写Linux内核模块的时候我们首先要清楚的知道内核模块的作用,为什么要使用内核模块?这是因为Linux内核的整体结构非常的庞大,包含的组件也非常的多,把所有的内核模块都编译到Linux内核,会导致的问题是内核很大,同时在现有的内核新增或删除部分功能时,不得不重新编译内核,可谓相当的费时。而模块的使用就是为了解决这一问题,即动态的在内核中添加或者删除相应功能。下面以一个经典的代码来看是相关的分析:

#include
#include

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("xxx");
MODULE_DESCRIPTION("Hello World Module");
MODULE_ALIAS("a simplest module");

static int __init hello_init()
{
    printk(KERN_EMERG"Hello World!\n");  //注意比较两种printk( )参数的不同之处
    return 0;                                                     //即优先级的用法
}

static void __exit hello_exit()
{
    printk("<6>hello exit\n");
}

module_init(hello_init);
module_exit(hello_exit);
在阅读相关的代码时我们的入口点不再是main( )函数,而应该是module_init( 函数名 )函数;细心的话就会发现在hello_init()函数前面有一个__init ,它的作用是在连接的时候把标识的函数放在 .init.text这个区段内。此外所有的__init函数在区段 .initcall.init中还保存了一个函数指针,在初始化的时候内核会通过这些函数指针调用这些__init 函数,在完成初始化后释放掉该区段。

printk( )函数和平printf( )函数类似,但是printk( )函数有打印优先级(数值越低,优先级越高),如果这个打印优先级比默认的低,那么将看不到输出,所以在使加载和卸载模块的过程中如果看不到打印语句,这是一个值得分析的问题。查看默认优先级的语句为:cat    /proc/sys/kenel/printk

在代码的起始部分我们给出了如下代码:

1.  MODULE_LICENSE("Dual BSD/GPL");
2.  MODULE_AUTHOR("xxx");
3.  MODULE_DESCRIPTION("Hello World Module");
4.  MODULE_ALIAS("a simplest module");

1.为必须选择的语句,它是模块的许可权限,如果不声明,会出现警告信息。

2.3.4为可选项,其为模块的一些相关信息。如作者、实现的功能等。

最后自然到了module_exit(hello_exit),有加载必须要有卸载,呵呵……做事要有始有终嘛,所以这自然就是相关的卸载部分了。其中的printk( )和前面的分析完全一样。

外加一点,如果模块有参数的传递,那么还需使用module_param(参数名,参数类型,参数的读/写权限),当参数为数组时,使用module_param_array(数组名,数组类型,数组长,参数读/写权限)。

懂了模块的编写,接下来就应该是模块的编译了,在当前路径下建立一个Makefile文件,编写如下代码:

ifneq ($(KERNELRELEASE),)

obj-m := hello.o           //hello为你编写的模块代码保存名(在此保存的为hello.c) ,编译生成目标文件hello.o

else
 
KDIR := /lib/modules/2.6.18-53.el5/build    //KDIR是本Makefile 依赖的linux内核源码路径  ,如是交叉编译时就取开发板上运行的源码路径
all:
 make -C $(KDIR) M=$(PWD) modules

 //到linux源码所在的目录执行主Makefile 并当前路径传给主Makefile,告诉主Makefile执行完后返回到当前目录,执行Makefile,
clean:
 rm -f *.ko *.o *.mod.o *.mod.c *.symvers

endif

KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile时, KERNELRELEASE没有被定义,所以make将读取执行else之后的内容。如果make的目标是clean,直接执行clean操作,然后结束。当make的目标为all时,-C $(KDIR) 指明跳转到内核源码目录下读取那里的Makefile;M=$(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile。当从内核源码目录返回时,KERNELRELEASE已被被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。else之前的内容为kbuild语法的语句, 指明模块源码中各文件的依赖关系,以及要生成的目标模块名。

该Makefile(注意Makefile中的M要大写,具体原因自己可查资料,在次就不一一讲解了)的编写一般都具有固定的格式,变化很少,可以当成模板来记。

最后通过insmod   hello.ko 和 rmmod   hello就可实现模块的安装和卸载了!!!

O(∩_∩)O~  最后总结一句:灯笼是张纸,只要你多思考多动手就OK了。

相关推荐