9 Linux内核模块设计
管理内核模块的命令
rmmod 移除已挂载模块
用法:rmmod 模块名(不带后缀) 等同于:modprobe -r 模块名
insmod 挂载模块
insmod 需要给出模块所在目录的绝对路径,以及要带有模块 文件名后缀(.o 或.ko)
功能上没有modprobe 强。
管理内核模块的命令
depmod 创建模块依赖关系的列表
设备驱动
模块 4
硬件平台
内核模块概述
什么是内核模块? 内核模块是一种没有经过链接,不能独立运行的目 标文件,是在内核空间中运行的程序。经过链接装载 到内核里面成为内核的一部分,可以访问内核的公用 符号(函数和变量)。 内核模块可以让操作系统内核在需要时载入和执行, 在不需要时由操作系统卸载。它们扩展了操作系统内 核的功能却不需要重新启动系统。 如果没有内核模块,我们不得不一次又一次重新编 译生成单内核操作系统的内核镜 像来加入新的功能。 这还意味着一个臃肿的内核。
内核模块概述
太多的设备驱动和内核功能集成在内核中,内核过于庞大。如
何解决?
Linux内核引入内核模块机制。通过动态加载内核模块,使得在运行过 程中扩展内核的功能。不需要的时候,卸载该内核模块。
libc
系统调用
模块
进储文
1
程 存 件 网络 多媒 模 模块
管 管 系 通讯 体 块 2
理理统
机 模块
制3
构建模块
模块代码编写好后,下一步就是编译模块了。 编译模块过程中的第一步是决定在那里管理模块源码:
你可以把模块源码加入到内核源代码树中; 也可以在内核源代码树之外维护和构建你的模块源码。
放在内核源代码树中
将模块源码放到内核源码的目录下去进行编译是最理 想的选择; 首先你要清楚你的模块应在内核源代码树中处于何处。 设备驱动程序存放在内核源码树根目录drivers/的子 目录下
CONFIG_HELLO_MODULE = m
放在内核源代码树中
添加编译目标 内核源码每个子目录下都有一个Makefile负责该目录 下的文件编译工作; Makefile中的目标:
obj-m += xxx.o 表示xxx.c文件编译成模块 obj-y+=xxx.o 表示xxx.c文件编译进内核映像文件uImage中
Linux内核模块设计
内核模块概述 内核模块代码结构 内核模块编译 管理内核模块的命令
教学内容
教学要求
了解内核模块机制的优缺点; 掌握内核模块程序的格式框架; 掌握内核模块入口、出口函数编写的格式; 掌握内核模块管理命令的使用;
内核模பைடு நூலகம்概述
Linux内核是整体式结构,各个子系统联系紧密,作为 一个大程序在内核空间运行。
Hello World 模块
hello_init()函数是模块的入口点,在模块装载时被调用。
module_init()实际上是一个宏调用,它惟一的参数便是模块的初始化 函数。模块的所有初始化函数必须符合下面的形式: int module_name_init(void);
▶该模块入口函数通常不会被外部函数直接调用,所以它一般被标记为 static类型。 ▶init函数会返回一个int型数值,如果初始化顺利完成,那么它的返回值为
模块源码已经放到内核中去了,但是内核的配置是用 make menuconfig来配置的,也就是说是通过配置菜单来 选择那些功能进行编译。 我们只是把模块源码放到内核里面去了,但是配置菜单上, 还没有相应的选项; 所以下一步是添加相应的配置选项; 配置菜单中的配置选项是来源与Kconfig文件的,内核源 码的每个子目录下都有Kconfig文件,负责提供菜单选项;
如果该模块被编译进内核,而不是动态加载,则宏 __init的使用会在初始化完成后丢弃该函数并收回所 占内存。 如果该模块被编译进内核,宏__exit将忽略“清理 收尾”的函数。 这些宏在头文件linux/init.h定义。
Hello World 模块
内核模块证书和内核模块文档说明
2.4内核后,引入识别代码是否在GPL许可下发布的机制 。 在使用非公开的源代码产品时会得到警告。通过宏 MODULE_LICENSE(“GPL”),设置模块遵守GPL证书,取 消警告信息。 宏MODULE_DESCRIPTION()用来描述模块的用途。 宏MODULE_AUTHOR()用来声明模块的作者。 这些宏都在头文件linux/module.h定义。使用这些宏只是 用来提供识别信息。
tristate 表示该选项有三种选择“Y”,“N”,“M”; default 表示默认的选择是什么 help 提示帮助信息 配置选项添加到相应的Kconfig文件后,我们在make menuconfig的时候就可以找到我们的配置菜单了。
放在内核源代码树中
make menuconfig 的配置菜单上选中我们的配置选项 Device drivers --> character devices -->
Hello World 模块
一个内核模块至少包含两个函数
初始化函数,在模块加载到内核时被调用。 卸载函数,在内核模块被卸载时被调用。 现在的模块“初始化”和“卸载”函数可以起任意的名 字。通过宏 module_init()声明初始化函数,通过 module_exit()声明卸载函数 。 任一个内核模块需要包含linux/module.h
类似地,卸载一个模块,你可使用rmmod命令。
rmmod module 比如,rmmod hello_module
管理内核模块的命令
lsmod 列出已经挂载的内核模块
lsmod 是列出目前系统中已加载的模块的名称及大小 等
效果跟通过less /proc/modules查看模块一样。
modinfo 查看模块信息
设备驱动文件被进一步按照类别、类型或特殊驱动程序等 更有序地组织起来。如 字符设备存在于drivers/char/目录下 而块设备存放在drivers/block/目录下 USB设备则存放在drivers/usb/目录下
例如:将我们的模块文件放到drivers/char目录下
放在内核源代码树中
添加相应的配置选项
放在内核源代码树中
现在,我们的模块代码hello_module.c已经添加到内核
源码中;
并且drivers/char/Kconfig添加了配置菜单;
在drivers/char/Makefile添加了目标;
下一步要做的工作就是编译内核:
make
#会编译所有东西
或者
make modules
#只编译模块
obj-m+=hello_module.o 可以使用如下的命令来编译模块:
make -C /home/sice/linux-3.5 M=/home/sice/hello_module modules
Makefile文件实例:
安装模块
编译后的模块文件(.ko)要加载到内核中,就必须将 内核放置到跟文件系统中,一般放置在跟文件系统的 lib/modules/version/目录下:
modinfo 可以查看模块的信息,通过查看模块信息来 判定这个模块的用途。
modinfo 模块名
管理内核模块的命令
modprobe 挂载新模块以及新模块相依赖的模块
modprobe 模块名,在挂载该内核模块的同时,这个模块所依 赖的模块也被同时挂载。
modprobe还有其他用法,问他的“man” 。例如:modprobe l 是列出内核中所有的模块,包括已挂载和未挂载的,读取的 模块列表就位于 /lib/modules/‘uname -r’ 目录中。
放在内核源代码树中
配置好后,保存退出配置菜单,所有的配置选项会保存 在.config隐藏文件中; 例如,我们在.config文件中查找配置选项如下:
我们找到CONFIG_HELLO_MODULE=m ; 也就是说Kconfig文件中的选项:
config HELLO_MODULE 在选择为模块后,保存在.config文件中的形式是:
内核模块概述
模块机制的优点:
减小内核映像尺寸,增加系统灵活性; 节省开发时间;修改内核,不必重新编译整个内核。 模块的目标代码一旦被链入内核,作用和静态链接的内 核目标代码完全等价。
模块机制的缺点:
对系统性能有一定损失; 使用不当时会导致系统崩溃;
Hello World 模块
Hello world模块,展示编写内核模块的基础
放在内核源码外
我们也可以将我们的模块代码hello_module.c放在内核源 码以外的任意地方进行编译; 例如:
内核源码目录:/home/sice/linux-3.5 我们新建一个目录来保存模块代码,模块代码放在: /home/sice/hello_module目录下 需要在这个目录下新建一个Makefile文件,文件的内容为:
目前的的Linux 发行版所用的内核是4.x版本,是自 动解决依赖关系。
depmod -a为所有列在/etc/modprobe.conf 或 /etc/modules.conf 中的所有模块创建依赖关系,并且 写入到modules.dep文件
depmod –e 列出已挂载但不可用的模块
课堂小结
如:我们的网络跟文件是/opt/rootfs,.ko文件就应该拷 贝到/opt/rootfs/lib/modules/linux-3.5-sice目录下;
模块的加载与卸载
加载模块最简单的方法是通过insmod命令。
insmod module 需要载入的模块名称由参数module指定,比如:
insmod hello_module.ko
例如,我们在drivers/char/Makefile文件中添加目 标:
上面的$(CONFIG_HELLO_MODULE)就是取.config文 件中的CONFIG_HELLO_MODULE的值;该值为m;