目录
本文使用环境:
内核版本:Linux 3.0.15 / iTop4412_Kernel_3.0
硬件平台:armv7 / itop-4412
编译环境:Ubuntu Linux 12.04 LTS / gcc version 4.4.1 (Sourcery G++ Lite 2009q3-67)
0. 引言
Linux
内核架构很庞大,包含的组件也非常多,如果静态地将所有功能全部编译到内核,编译出的内核镜像文件将很大,且一旦有部分功能需要修改,每一次修改将不得不重新编译内核。Linux
中支持以模块加载的方式动态地将包含特定功能的模块
加载到内核,模块
一旦被成功加载,就和内核中其他模块
一样。模块
机制分离了内核中相对固定的部分和变化的部分,给调试带来了极大的便利,这种机制类似VxWorks
中的模块加载
机制。
1. linux内核模块
1.1 内核模块代码
下面给出一个最简单的内核模块simple_module.c
:
#include <linux/init.h>
#include <linux/module.h>
static int __init simple_module_init(void)
{
printk(KERN_INFO "%s called\n", __FUNCTION__);
return 0;
}
static int __exit simple_module_exit(void)
{
printk(KERN_INFO "%s called\n", __FUNCTION__);
return 0;
}
module_init(simple_module_init);
module_exit(simple_module_exit);
MODULE_AUTHOR("ryan");
MODULE_LICENSE("GPL v2");
1.2 内核模块组成
该模块主要包含了模块加载函数、模块卸载函数、模块GPL v2
许可声明三大部分,其他部分如:MODULE_AUTHOR
、EXPORT_DESCRIPTION
、EXPORT_SYMBOL
、EXPORT_SYMBOL_GPL
为可选项,非必须。
- 模块被加载时执行由
module_init
声明的模块加载函数simple_module_init
; - 模块被卸载时执行由
module_exit
声明的模块卸载函数simple_module_exit
; MODULE_LICENSE("GPL v2");
语句声明该模块使用的许可证类型,可用其他LICENSE
如"GPL"
、"GPL and additional rights"
、"Dual MIT/GPL"
、"Dual BSD/GPL"
、"Dual MPL/GPL"
和"Proprietary"
详情见官方文档。当模块中没有LICENSE
声明时,加载模块将显示内核被污染的警告:
[root@iTOP-4412]# insmod simple_module.ko
[ 2294.055272] simple_module: module license 'unspecified' taints kernel.
[ 2294.060472] Disabling lock debugging due to kernel taint
[ 2294.068879] simple_module_init
模块使用了printk
函数,该函数用于内核空间数据输出,类似于用户空间中使用的printf
,printk
函数可以定义消息打印级别,如simple_module.c
中使用的KERN_INFO
宏,该宏定义在内核源码目录/include/linux/printk.h
文件中定义,使用printk
函数时可根据实际情况选择不同的打印级别:
1.3 修改Makefile
要将simple_module.c
文件编译成模块还需要linux
内核源码,本文使用的内核版本为linux 3.0.15
,在当前simple_module.c
源码路径使用如下Makefile
文件编译simple_module.c
,该Makefile
将调用内核目录中的Makefile
完成simple_module
模块编译
#!/bin/bash
#将simple_module.c这个文件编译成中间文件simple_module.o
obj-m += simple_module.o
#linux内核源码路径
KDIR := /home/topeet/android4.0/iTop4412_Kernel_3.0
#当前目录变量
PWD ?= $(shell pwd)
#make命名默认寻找第一个目标
#make -C就是指调用执行的路径
#$(KDIR)Linux源码目录,当前环境指的是/home/topeet/android4.0/iTop4412_Kernel_3.0
#$(PWD)当前目录变量
#modules要执行的操作
all:
make -C $(KDIR) M=$(PWD) modules
#make clean执行的操作是删除后缀为o的文件
clean:
rm -rf *.o *.ko *.mod.c *.order *.symvers
1.4 模块编译
Makefile
修改完成后,在simple_module.c
源码路径下执行make
命令编译模块,编译完成后将在simple_module.c
源码路径下产生simple_module.ko
文件,该文件即待加载的模块
文件
当前itop4412
开发板使用NFS
根文件系统启动,将simple_module.ko
文件拷贝到根文件系统目录中,开发板上电后自动挂载NFS
根文件系统,进入相应的目录即可访问simple_module.ko
文件。
1.5 使用insmod/rmmod命令加载/卸载模块
启动开发板,进入/system/drvbin/
路径执行insmod simple_module.ko
安装模块:
模块加载函数simple_module_init
被调用。
执行rmmod simple_module
卸载模块:
模块卸载函数simple_module_exit
被调用。
模块加载后可通过lsmod
命令查看已加载的模块,执行cat /proc/modules
命令也可以得到相同的结果。
1.6 使用modprobe/modprobe -r命令加载/卸载模块
模块的加载和卸载也可以使用modprobe <模块名>
和modprobe -r <模块名>
命令,使用modprobe
命令加载模块时将同时加载该模块所依赖的模块,使用modprobe -r
的方式卸载模块时也会同时卸载该模块所依赖的模块。
使用modprobe
命令需要先将模块文件拷贝到/lib/modules/'uname -r'
路径下,在当前环境下'uname -r'
返回的结果为3.0.15
:
即modprobe
的探测路径为/lib/modules/3.0.15/
,该路径下还需要一个modules.dep
文件,不存在这个文件时用touch modules.dep
命令新建一个即可,将simple_module.ko
模块文件拷贝到/lib/modules/3.0.15/
路径下:
经过前面的操作,执行modprobe
命令的条件已经具备,执行modprobe simple_module
(不需要文件扩展名)命令加载模块:
执行modprobe -r simple_module
命令卸载模块:
使用modprobe [-r]
命令和insmod/rmmod
命令得到的结果并无二致。
2. 小结
本文首先以linux
内核模块必要组成部分的角度,给出一个简单的内核模块代码,接着分析一个内核模块的基本组成,并简要介绍了内核模块的编译方法,最后通过两组命令描述内核模块加载与卸载的方法。
参考
- 《Linux设备驱动开发详解-基于最新的Linux4.0内核》第4章
- 《itop-4412开发板之精英版使用手册_v4.0》第11章