Linux2.6内核模块设计内核模块概述Linux内核是整体式结构,各个子系统联系紧密,作为一个大程序在内核空间运行。内核模块概述太多的设备驱动和内核功能集成在内核中,内核过于庞大。如何解决?Linux内核引入内核模块机制。通过动态加载内核模块,使得在运行过程中扩展内核的功能。不需要的时候,卸载该内核模块。进程管理储存管理文件系统网络通讯多媒体设备驱动模块机制系统调用硬件平台模块1模块2模块3模块4libc内核模块概述什么是内核模块?内核模块是一种没有经过链接,不能独立运行的目标文件,是在内核空间中运行的程序。经过链接装载到内核里面成为内核的一部分,可以访问内核的公用符号(函数和变量)。内核模块可以让操作系统内核在需要时载入和执行,在不需要时由操作系统卸载。它们扩展了操作系统内核的功能却不需要重新启动系统。如果没有内核模块,我们不得不一次又一次重新编译生成单内核操作系统的内核镜像来加入新的功能。这还意味着一个臃肿的内核。内核模块概述模块机制的优点:减小内核映像尺寸,增加系统灵活性;节省开发时间;修改内核,不必重新编译整个内核。模块的目标代码一旦被链入内核,作用和静态链接的内核目标代码完全等价。模块机制的缺点:对系统性能有一定损失;使用不当时会导致系统崩溃;内核模块概述内核模块是如何被调入内核工作的?当操作系统内核需要的扩展功能不存在时,内核模块管理守护进程kmod执行modprobe去加载内核模块。modprobe遍历文件/lib/modules/$(version)/modules.dep来判断是否有其它内核模块需要在该模块加载前被加载。最后modprobe调用insmod先加载被依赖的模块,然后加载该被内核要求的模块。内核模块概述内核模块的卸载当我们不需要内核模块了,为了减少系统资源的开销,需要卸载时使用命令#rmmodmodule_name或者#modprobe–rmodule_name查看系统已经加载的模块,使用命令#lsmod实验:HelloWorld模块步骤:新建模块目录用编辑器(vi)编辑源文件用编辑器编辑Makefile在内核源码树外编译把模块加到内核源码树并把配置信息同时加入实验:HelloWorld模块#includelinux/init.h//formodule_init()#includelinux/module.h//mustbeinclude#includelinux/kernel.h//forprintk()staticint__inithello_init(void){printk(“Helloworld\n”);return0;}staticvoid__exithello_exit(void){printk(“Hellomoduleexit\n”);}module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE(“GPL”);MODULE_AUTHOR(“hsq”);实验:HelloWorld模块说明:1)模块入口函数为hello_init(),由module_init()宏指定,在模块被加载的时候被调用向系统注册,就象应用程序的main()一样,主要来完成模块的初始化工作2)入口函数的返回值为0表示成功,非0表示失败3)模块的退出函数为hello_exit(),由module_exit()宏指定,在模块被卸载是被调用向系统注销,主要来完成资源的清理工作,它被调用完毕后,就模块就被内核清除了4)一个模块最少需要有入口和退出函数实验:HelloWorld模块说明:关于__init和__exit宏如果该模块被编译进内核,而不是动态加载,则宏__init的使用会在初始化完成后丢弃该函数并收回所占内存。如果该模块被编译进内核,宏__exit将忽略“清理收尾”的函数。这些宏在头文件linux/init.h定义,用来释放内核占用的内存。例如启动时看到的信息“Freeingunusedkernelmemory:236kfreed”,正是内核释放这些函数所占用空间时的打印信息。实验:HelloWorld模块printk()函数printk函数在Linux内核中定义并且对模块可用,为内核提供日志功能,记录内核信息或用来给出警告。与标准C库函数printf的行为相似。每个printk()声明都会带一个优先级。内核总共定义了八个优先级的宏,在linux/kernel.h中定义。若你不指明优先级,DEFAULT_MESSAGE_LOGLEVEL这个默认优先级将被采用。信息添加到文件/var/log/messages,可直接查看,或者用命令dmesg查看。在X-windows下的终端insmod一个模块,日志信息只会记录在日志文件中,而不在终端打印。实验:HelloWorld模块写内核程序需要注意:实验:HelloWorld模块内核模块的Makefile:CONFIG_HELLO_WORLD?=mifneq($(KERNELRELEASE),)hello_world-objs:=hello.oobj-$(CONFIG_HELLO_WORLD)+=hello_world.oelseKERNELDIR=/root/gec2410-linux-2.6.8.1/PWD:=$(shellpwd)modules:$(MAKE)-C$(KERNELDIR)M=$(PWD)modulesendifclean:rm–rf*.o*~core.depend.*.cmd*.ko*.mod.c.tmp_versions实验:HelloWorld模块Makefile说明:为2.6版本内核构造模块首先需要有配置并构建好的2.6内核源代码树。而且最好运行和模块对应的内核。2.6内核的模块要和内核源代码树中的目标文件连接。2.6内核的构建系统Kbuild,使得内核源码外的内核模块编译跟内核编译统一起来,无须手动给定这些参数。改变目录到用-C选项提供的内核源码目录,在那里找到内核的顶层makefile。M=选项使makefile在试图建立模块目标前,回到模块源码目录。注意:hello.c文件中双引号“”为中文的,在文件中要使用英文的,需改正Makefile中格式需正确,指向的linux内核文件夹必须存在并正确编译过。实验:HelloWorld模块编译加载/卸载:在helloworld模块目录上#make得到hello_world.ko就是产生的内核模块,在评估板上使用#insmodhello_world.ko//加载模块和#rmmodhello_world//卸载模块观察控制台输出的结果实验:HelloWorld模块内核模块证书和内核模块文档说明2.4内核后,引入识别代码是否在GPL许可下发布的机制。在使用非公开的源代码产品时会得到警告。通过宏MODULE_LICENSE(“GPL”),设置模块遵守GPL证书,取消警告信息。宏MODULE_DESCRIPTION()用来描述模块的用途。宏MODULE_AUTHOR()用来声明模块的作者。宏MODULE_SUPPORTED_DEVICE()声明模块支持的设备。这些宏都在头文件linux/module.h定义。使用这些宏只是用来提供识别信息。实验:HelloWorld模块模块参数内核允许对模块指定参数,这些参数可在装载模块时改变。在运行insmod或者modprobe命令时给出参数的值。insmodhellop.kohowmany=10whom=Mom如何定义实现模块参数呢?要传递参数给模块,首先将获取参数值的变量声明为全局变量。然后使用宏moudle_param来声明intmyint=3;module_param(myint,int,0);实验:HelloWorld模块模块参数module_param(name,type,perm);perm是一个权限值,控制谁可以存取模块参数在sysfs中的表示。perm被设为0,就根本没有sysfs项这个宏定义应当放在任何函数之外,典型地是出现在源文件的前面。应该总是为变量赋初值。实验:HelloWorld模块模块参数宏MODULE_PARM_DESC()用来注解该模块可以接收的参数。该宏两个参数:变量名和一个对该变量的描述。模块可以用这样的命令行加载:./insmodmymodule.komyvariable=2实验:HelloWorld模块模块参数声明一个数组参数:module_param_array(name,type,num,perm);•name数组的名子(也是参数名)•type数组元素的类型•num是数组元素的个数,模块加载者拒绝比数组能放下的多的值。2.6.9传递数组个数变量名,2.6.11传递数组个数变量的地址。•perm是通常的权限值.如果数组参数在加载时设置。实验:HelloWorld模块参数数组的定义:staticinttest[5]={1,2,3,4,5};staticintnum=5;module_param(num,int,0);module_param_array(test,int,num,0);MODULE_PARM_DESC(test,testarray);参数数组的加载方式:insmodtest.kotest=6,7,8,9,10num=5实验:HelloWorld模块把模块加到Kernel的代码树:选择一个需要把你的模块加入进去的Kernel子目录,如/$(KERNEL_DIR)/drivers/char,在此子目录的Makefile中的适当的位置加入obj-$(CONFIG_HELLO_WORLD)+=$(your_module_dir_name)/这一句让Kernel被Kbuild系统编译的时候进入到您的模块的Makefile去执行实验:HelloWorld模块把模块加到Kernel的代码树:在模块的目录中新建一个Kconfig文件,内容为configHELLO_WORLDtristate“Ahelloworldsample”defaultmhelpthisisatestmodulesampleforstudy,itjustprinthelloworldonconsole在/$(KERNEL_DIR)/drivers/char/Kconfig中的适当位置加入source“drivers/char/$(your_module_dir_name)/Kconfig”实验:HelloWorld模块修改模块的Makefile:hello_world-objs:=hello.oobj-$(CONFIG_HELLO_WORLD):=hello_world.o注意Kconfig里面HELLO_WORLD前面没有CONFI_的,是Kbuild系统在编译时加上的重新配置和编译内核,看helloworld是否在系统启动时有出现实验:HelloWorld模块Kconfig的知