实验五内核模块设计实验文章由网提供现代的Linux内核是具有微内核特点的宏内核。
Linux内核作为一个大程序在内核空间运行。
太多的设备驱动和内核功能集成在内核中,内核过于庞大。
Linux内核引入内核模块机制。
通过动态加载内核模块,使得在运行过程中扩展内核的功能。
不需要的时候,卸载该内核模块。
内核模块内核模块是一种没有经过链接,不能独立运行的目标文件,是在内核空间中运行的程序。
经过链接装载到内核里面成为内核的一部分,可以访问内核的公用符号(函数和变量)。
内核模块可以让操作系统内核在需要时载入和执行,在不需要时由操作系统卸载。
它们扩展了操作系统内核的功能却不需要重新启动系统。
如果没有内核模块,我们不得不一次又一次重新编译生成单内核操作系统的内核镜像来加入新的功能。
这还意味着一个臃肿的内核。
内核模块是如何被调入内核工作的?当操作系统内核需要的扩展功能不存在时,内核模块管理守护进程kmod执行modprobe去加载内核模块。
modprobe遍历文件/lib/modules/version/modules.dep 来判断是否有其它内核模块需要在该模块加载前被加载。
最后modprobe调用insmod先加载被依赖的模块,然后加载该被内核要求的模块。
内核模块是如何被调入内核工作的?Insmod将调用init_module系统调用,传入参数(Module.c)Sys_init_module系统调用检查权限后,并查找modules链表,验证模块未被链接。
然后分配一个module结构体变量描述该内核模块。
如果定义了模块的init方法,则执行init方法。
模块机制的特点:减小内核映像尺寸,增加系统灵活性;节省开发时间;修改内核,不必重新编译整个内核。
模块的目标代码一旦被链入内核,作用和静态链接的内核目标代码完全等价。
最简单的内核模块任一个内核模块需要包含linux/module.h初始化函数init_module(),在模块加载到内核时被调用。
init_module()向内核注册模块的功能,申请需要的资料等初始化工作。
卸载函数cleanup_module() ,在内核模块被卸载时被调用,干一些收尾清理的工作,撤消任何初始化函数init_module()做的事,保证内核模块可以被安全的卸载。
printk( )函数printk 函数在Linux 内核中定义并且对模块可用,为内核提供日志功能,记录内核信息或用来给出警告。
与标准 C 库函数printf 的行为相似。
每个printk() 声明都会带一个优先级。
内核总共定义了八个优先级的宏,在linux/kernel.h中定义。
若你不指明优先级,DEFAULT_MESSAGE_LOGLEVEL 这个默认优先级将被采用。
信息存在在内核消息缓冲区中,并被定时的添加到文件/var/log/messages,可直接查看,或者用命令dmesg查看。
在X-windows下的终端insmod一个模块,日志信息只会记录在日志文件中,而不在终端打印。
从内核Linux 2.4之后,可以为模块的“初始化”和“卸载”函数起任意的名字。
不再必须使用init_module()和cleanup_module()的名字。
通过宏module_init()和module_exit()实现。
这些宏在linux/init.h中定义。
module_init(hello_2_init);module_exit(hello_2_exit);函数必须在宏的使用前定义,否则编译会报错。
关于__init和__exit宏如果该模块被编译进内核,而不是动态加载,则宏__init的使用会在初始化完成后丢弃该函数并收回所占内存。
如果该模块被编译进内核,宏__exit将忽略“清理收尾”的函数。
这些宏在头文件linux/init.h定义,用来释放内核占用的内存。
例如启动时看到的信息“Freeing unused kernel memory: 236k freed”,正是内核释放这些函数所占用空间时的打印信息。
内核模块证书和内核模块文档说明2.4内核后,引入识别代码是否在GPL许可下发布的机制。
在使用非公开的源代码产品时会得到警告。
通过宏MODULE_LICENSE(“GPL”),设置模块遵守GPL证书,取消警告信息。
宏MODULE_DESCRIPTION()用来描述模块的用途。
宏MODULE_AUTHOR()用来声明模块的作者。
宏MODULE_SUPPORTED_DEVICE() 声明模块支持的设备。
这些宏都在头文件linux/module.h定义。
使用这些宏只是用来提供识别信息。
准备工作顺利编译并且加载第一个“hello world”模块有时会比较困难。
保证系统具备正确的编译器、模块工具、以及其他必要工具。
内核目录Documentation/Changes 列出了需要的工具版本。
用错误的工具版本建立一个内核(包括模块),可能导致一些奇怪复杂的问题。
通常芯片公司的SDK包会告诉你使用什么版本的交叉工具链去完成,并提供相应的工具链。
准备好系统平台所对应的内核源代码配置并构建好平台对应的2.6内核源代码。
2.6 内核使用kbuild构建系统配置编译,kbuild构建系统可用于编译自定义的内核模块。
编译过程首先会到内核源码目录下,读取顶层的Makefile文件,然后再编译模块源码,连接生成的内核模块后缀为.ko在内核源代码目录下:make menuconfig,使得配置跟目标平台一致make编写内核模块的makefile内核Makefile提供的obj-m表示对象文件(object files)编译成可加载的内核模块Hello-1.c的Makefile文件obj-m += hello-1.o表明有一个模块要从目标文件hello-1.o 建立,kbuild从该目标文件建立内核模块hello-1.ko。
编译内核模块的命令make -C 内核源代码路径M=模块所在路径modulesmake -C /lib/modules/`uname -r`/build M=$PWD modules改变目录到用-C 选项提供的内核源码目录,make读取内核的makefile,并编译M所指定路径下的内核模块源代码。
内核模块的加载2.6内核模块使用.ko的文件后缀(代替.o后缀)。
使用insmod ./hello-1.ko命令加载该模块。
/proc/modules记录被加载的内核模块。
使用lsmod命令查看已经加载的模块使用命令rmmod hello-1 卸载模块tail /var/log/messages编写hello模块,包含初始化init_module和卸载函数cleanup_module编写makefile文件Obj-m += hello-1.o编译hello内核模块make -C /lib/modules/`uname -r`/build M=$PWD modulesUname –r 给出当前系统内核的版本,使用短撇号,将其输出结果作为参数的一部分。
而build是符号链接,指向对应内核的源代码目录。
加载hello内核模块:insmod hello-1.ko查看加载信息:tail /var/log/message卸载hello内核模块rmmod hello-1使用modprobe命令修改/lib/module /`uname -r`/modules.dep文件,添加hello-1.ko内核模块路径和依赖关系加载hello内核模块: Modprobe hello-1卸载hello内核模块Modprobe –r hello-1⏹修改hello内核模块的makefile⏹添加all目标⏹添加clean目标obj-m += hello-1.oKERN_VER = $(shell uname -r)KERN_DIR = /lib/modules/$(KERN_VER)/buildall:$(MAKE) -C $(KERN_DIR) M=$(PWD) modulesclean:rm -rf *.orm -rf *.mod.*rm -rf *.korm –rf .tmp_version⏹直接通过make,编译内核模块⏹多个文件构成的内核模块⏹Makefile会帮我们完成编译和连接的工作。
⏹例如内核模块分两个文件start.c stop.c,则Makefile这样写:obj-m += test.otest-objs := start.o stop.o跟单个文件模块的编译方式一样,内核编译系统会将所有的目标文件连接为一个文件。
⏹内核模块的Makefile模块mymodule-objs = file1.o file2.oobj-m += mymodule.oPWD = $(shell pwd)KDIR = 内核源代码路径all:$(MAKE) -C $(KDIR) M=$(PWD) modulesclean:rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versionslsmod 列出已经挂载的内核模块lsmod 是列出目前系统中已加载的模块的名称及大小等效果跟通过less /proc/modules查看模块一样。
modinfo 查看模块信息modinfo 可以查看模块的信息,通过查看模块信息来判定这个模块的用途。
modinfo 模块名modprobe 挂载新模块以及新模块相依赖的模块modprobe 模块名,在挂载该内核模块的同时,这个模块所依赖的模块也被同时挂载。
modprobe还有其他用法,问他的“man”。
例如:modprobe -l 是列出内核中所有的模块,包括已挂载和未挂载的,读取的模块列表就位于/lib/modules/‘uname -r’目录中。
rmmod 移除已挂载模块用法:rmmod 模块名(不带后缀)等同于:modprobe -r 模块名insmod 挂载模块insmod 需要给出模块所在目录的绝对路径,以及要带有模块文件名后缀(.o 或.ko)insmod /lib/modules/2.6.18/kernel/fs/vfat/vfat.ko功能上没有modprobe 强。
depmod 创建模块依赖关系的列表目前的的Linux 发行版所用的内核是2.6x版本,是自动解决依赖关系。
depmod -a为所有列在/etc/modprobe.conf 或/etc/modules.conf 中的所有模块创建依赖关系,并且写入到modules.dep文件depmod –e 列出已挂载但不可用的模块为2410开发板配置编译Linux内核内核版本:linux-2.6.24交叉工具链:cross-3.4.4.tar.gz选择相应的配置时,有三种选择,它们分别代表的含义如下:Y--将该功能编译进内核N--不将该功能编译进内核M--将该功能编译成模块,可以在需要时动态插入到内核中make xconfig,使用鼠标就可以选择对应的选项。