资源描述
Click to edit Master text styles,Second level,Third level,Fourth level,Fifth level,*,Click to edit Master title style,Linux 2.6,内核模块设计,黄松青,内核模块概述,Linux,内核是整体式结构,各个子系统联系紧密,作为一个大程序在内核空间运行。,内核模块概述,太多的设备驱动和内核功能集成在内核中,内核过于庞大。如何解决?,Linux,内核引入内核模块机制。通过动态加载内核模块,使得在运行过程中扩展内核的功能。不需要的时候,卸载该内核模块。,内核模块概述,什么是内核模块?,内核模块是一种,没有经过链接,不能独立运行的目标文件,,是在内核空间中运行的程序。经过链接装载到内核里面成为内核的一部分,可以访问内核的公用符号(函数和变量)。,内核模块可以让操作系统内核,在需要时载入和执 行,在不需要时由操作系统卸载,。它们扩展了操作系统内核的功能却不需要重新启动系统。,如果没有内核模块,我们不得不一次又一次重新编译生成单内核操作系统的内核镜 像来加入新的功能。这还意味着一个臃肿的内核。,内核模块概述,模块机制的优点:,减小内核映像尺寸,增加系统灵活性;,节省开发时间;修改内核,不必重新编译整个内核。,模块的目标代码一旦被链入内核,作用和静态链接的内核目标代码,完全等价,。,模块机制的缺点:,对系统性能有一定损失;,使用不当时会导致系统崩溃;,内核模块概述,内核模块是如何被调入内核工作的,?,当操作系统内核需要的扩展功能不存在时,内核模块管理守护进程,kmod,执行,modprobe,去加载内核模块。,modprobe,遍历文件,/lib/modules/$(version)/,modules.dep,来判断是否有其它内核模块需要在该模块加载前被加载。,最后,modprobe,调用,insmod,先加载被依赖的模块,然后加载该被内核要求的模块。,内核模块概述,内核模块的卸载,当我们不需要内核模块了,为了减少系统资源的开销,需要卸载时使用命令,#,rmmod,module_name,或者,#,modprobe,r,module_name,查看系统已经加载的模块,使用命令,#,lsmod,实验,:Hello World,模块,步骤,:,新建模块目录,用编辑器,(vi),编辑源文件,用编辑器编辑,Makefile,在内核源码树外编译,把模块加到内核源码树并把配置信息同时加入,实验,:Hello World,模块,#include /for,module_init,(),#include /must be include,#include /for,printk,(),static,int,_init,hello_init(void,),printk(“Hello,worldn”);,return 0;,static void _exit,hello_exit(void,),printk(“Hello,module exitn”);,module_init(hello_init,);,module_exit(hello_exit,);,MODULE_LICENSE(“GPL”);,MODULE_AUTHOR(“hsq,”);,实验,:Hello World,模块,说明,:,1),模块入口函数为,hello_init,(),由,module_init,(),宏指定,在模块被加载的时候被调用向系统,注册,就象应用程序的,main(),一样,主要来,完成模块的初始化工作,2),入口函数的返回值为,0,表示成功,非,0,表示失败,3),模块的退出函数为,hello_exit,(),由,module_exit,(),宏,指定,在模块被卸载是被调用向系统注销,主要来完成,资源的清理工作,它被调用完毕后,就模块就被内核清除了,4),一个模块最少需要有入口和退出函数,实验,:Hello World,模块,说明,:,关于,_init,和,_exit,宏,如果该模块被编译进内核,而不是动态加载,则宏,_init,的使用会在初始化完成后丢弃该函数并收回所占内存。,如果该模块被编译进内核,宏,_exit,将忽略“清理收尾”的函数。,这些宏在头文件,linux/init.h,定义,用来释放内核占用的内存。例如启动时看到的信息“,Freeing unused kernel memory:236k freed,”,,正是内核释放这些函数所占用空间时的打印信息。,实验,:Hello World,模块,printk,(),函数,printk,函数在,Linux,内核中定义并且对模块可用,为内核提供日志功能,记录内核信息或用来给出警告。与标准,C,库函数,printf,的行为相似。,每个,printk,(),声明都会带一个优先级。内核总共定义了八个优先级的宏,在,linux/kernel.h,中定义。若你不指明优先级,,DEFAULT_MESSAGE_LOGLEVEL,这个默认优先级将被采用。,信息添加到文件,/,var,/log/messages,,可直接查看,或者用命令,dmesg,查看。在,X,windows,下的终端,insmod,一个模块,日志信息只会记录在日志文件中,而不在终端打印。,实验,:Hello World,模块,写内核程序需要注意,:,实验,:Hello World,模块,内核模块的,Makefile:,CONFIG_HELLO_WORLD?=m,ifneq,($(KERNELRELEASE),),hello_world-objs,:=,hello.o,obj-$(CONFIG_HELLO_WORLD),+=,hello_world.o,else,KERNELDIR=,/root/gec2410-linux-2.6.8.1/,PWD:=$(shell,pwd,),modules:,$(MAKE)-C$(KERNELDIR)M=$(PWD)modules,endif,clean:,rm,rf,*.o,*,core,.,depend,.*.,cmd,*.,ko,*.,mod.c,.,tmp_versions,实验,:Hello World,模块,Makefile,说明,:,为,2.6,版本内核构造模块,首先需要有配置并构建好的,2.6,内核源代码树。而且最好运行和模块对应的内核。,2.6,内核的模块要和内核源代码树中的目标文件连接。,2.6,内核的构建系统,Kbuild,,使得内核源码外的内核模块编译跟内核编译统一起来,无须手动给定这些参数。,改变目录到用,-C,选项提供的内核源码目录,在那里找到内核的顶层,makefile,。,M=,选项使,makefile,在试图建立模块目标前,回到模块源码目录。,实验,:Hello World,模块,编译加载,/,卸载,:,在,hello world,模块目录上,#make,得到,hello_world.ko,就是产生的内核模块,在,评估板上使用,#,insmod,hello_world.ko,和,#,rmmod,hello_world,观察控制台输出的结果,实验,:Hello World,模块,内核模块证书和内核模块文档说明,2.4,内核后,引入识别代码是否在,GPL,许可下发布的机制。在使用非公开的源代码产品时会得到警告。通过宏,MODULE_LICENSE(“GPL”),,设置模块遵守,GPL,证书,取消警告信息。,宏,MODULE_DESCRIPTION(),用来描述模块的用途。,宏,MODULE_AUTHOR(),用来声明模块的作者。,宏,MODULE_SUPPORTED_DEVICE(),声明模块支持的设备。,这些宏都在头文件,linux/module.h,定义。使用这些宏只是用来提供识别信息。,实验,:Hello World,模块,模块参数,内核允许对模块指定参数,这些参数可在装载模块时改变。在运行,insmod,或者,modprobe,命令时给出参数的值。,insmod,hellop.ko,howmany,=10 whom=Mom,如何定义实现模块参数呢?,要传递参数给模块,首先将获取参数值的变量声明为全局变量。然后使用宏,moudle_param,来声明,int,myint,=3;,module_param,(myint,int,,,0);,实验,:Hello World,模块,模块参数,module_param(name,type,perm,);,perm,是一个权限值,控制谁可以存取模块参数在,sysfs,中的表示。,perm 被设为 0,就根本没有 sysfs 项,这个宏定义应当放在任何函数之外,典型地是出现在源文件的前面。,应该总是为变量赋初值。,实验,:Hello World,模块,模块参数,宏,MODULE_PARM_DESC(),用来注解该模块可以接收的参数。该宏两个参数:变量名和一个对该变量的描述。,模块可以用这样的命令行加载:,./,insmod,mymodule.ko,myvariable,=2,实验,:Hello World,模块,模块参数,声明一个数组参数:,module_param_array(name,type,num,perm,);,name,数组的名子,(,也是参数名,),type,数组元素的类型,num,是数组元素的个数,模块加载者拒绝比数组能放下的多的值。,2.6.9,传递数组个数变量名,,2.6.11,传递数组个数变量的地址。,perm,是通常的权限值,.,如果数组参数在加载时设置。,实验,:Hello World,模块,参数数组的定义:,static,int,test5=1,2,3,4,5;,static,int,num=5;,module_param(num,int,0);,module_param_array(test,int,num,0);,MODULE_PARM_DESC(test,test array);,参数数组的加载方式:,insmod,test.ko,test=6,7,8,9,10 num=5,实验,:Hello World,模块,把模块加到,Kernel,的代码树,:,选择一个需要把你的模块加入进去的,Kernel,子目录,如,/$(KERNEL_DIR)/drivers/char,在此子目录的,Makefile,中的,适当的位置加入,obj-$(CONFIG_HELLO_WORLD),+=,$(your_module_dir_name)/,这一句让,Kernel,被,Kbuild,系统编译的时候进入到您的模块的,Makefile,去执行,实验,:Hello World,模块,把模块加到,Kernel,的代码树,:,在模块的目录中新建一个,Kconfig,文件,内容为,config,HELLO_WORLD,tristate,“A,helloworld,sample”,default m,help,this is a test module sample for,study,it just print hello world on,console,在,/$(KERNEL_DIR)/drivers/char/,Kconfig,中的适当位置加入,source“drivers/char/$(,your_module_dir_name)/Kconfig,”,实验,:Hello World,模块,修改模块的,Makefile,:,hello_world-objs,:=,hello.o,obj,-$(CONFIG_HELLO_WORLD):=,hello_world.o,注意,Kconfig,里面,HELLO_WORLD,前面没有,CONFI_,的,是,Kbuild,系统在编译时加上的,从新配置和编译内核,看,hello world,是否,在系统启动时有出现,实验,:Hello World,模块,Kconfig,的知识,:,tristate,表示三态的,对应的选项有,y,m,n,bool,是布尔型的,对应的选项有,y,n,depend on OTHER_CONFIG-,可以通过这重方式来表示,这个模块是依赖于其他模块选项的,也可以通过如,bool,“a sample”if OTHER_CONFIG,的方法来做,感兴趣的话可以通过,$(KERNEL_DIR)/Documents/,kbuild,/,kconfig_language.txt,来了解更详细的信息,谢谢!,
展开阅读全文