linux内核模块
linux内核模块机制:动态将组件加入到内核
内核模块本身并不被编译进内核文件(zImage或bzImage),在内核运行期间动态的安装或卸载
内核模块的程序结构:
- 模块加载函数(必须):module_init()的宏来指定
- 模每一种y块卸载函数(必须):module_exit()的宏来指定
每一种语言第一次都是从hello world开始的,内核模块也不例外
下面是一个简单的内核模块程序
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
这个模块定义了两个函数, 一个在模块加载到内核时被调用( hello_init )以及一个在模
块被去除时被调用( hello_exit ). moudle_init 和 module_exit 这几行使用了特别的内
核宏来指出这两个函数的角色. 另一个特别的宏 (MODULE_LICENSE) 是用来告知内核, 该模块带有一个自由的许可证; 没有这样的说明, 在模块加载时内核会抱怨。
printk 函数在 Linux 内核中定义并且对模块可用; 它与标准 C 库函数 printf 的行为相
似. 内核需要它自己的打印函数, 因为它靠自己运行, 没有 C 库的帮助. 模块能够调用
printk 是因为, 在 insmod 加载了它之后, 模块被连接到内核并且可存取内核的公用符号
字串 KERN_ALERT 是消息的优先级.
linux模块的安装与卸载命令
- 加载 insmod (insmod hello.ko)
- 卸载 rmmod (rmmod hello)
- 查看 lsmod
- 加载 modprobe (modprobe hello)
modprobe如同insmod,也是加载一个模块到内核,他们的不同之处在于它会根据文件系统 /lib/modules/($version)/module.dep 来查看加载的模块,看它们还依赖于其他模块,如果是:modprobe会先找到这些模块,把它们加载到内核
模块还有一些可选的信息:
- 作者: MODULE_AUTHOR(" ");
- 描述: MODULE_DESCRIPTION(" ");
- 模块版本: MODULE_VERSION(" ");
- 别名: MODULE_ALIAS(" ");
模块的参数:
- module_param:模块参数用于加载模块时的传递参数给模块,类似int main(int argc,char **argv)
module_param(name,type,perm)
参数:
name:模块参数名称
type :参数类型
perm:参数访问权限
perm常见值:
- S_IRUGO :任何用户对 /sys/module 中出现的参数有读权限
- S_IWUSR:允许root用户修改 /sys/module 中出现的该参数
hello.ko内核模块的安装与卸载现象
编写过程序之后需要编写Makefile,如下:
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /home/zhangbin/mini6410/linux-2.6.38
all:
make -C $(KDIR) M=$(PWD)
clean:
rm -f *.ko *.o *.mod.c *.symvers *.order
endif
执行make 后会有hello.ko文件.效果如下:
root@xxx#: insmod ./hello.ko
Hello,world
root@xxx#: rmmod hello
Goodbye cruel world
root@xxx#:
但是在内核模块加载中也会出现问题:常见的问题是:版本不匹配:
解决方法;
- 使用modprobe --force -modversion 强行插入 (不推荐)
- 确保编译内核模块时,所依赖的内核代码版本等同与当前正在运行的内核
(uname -r 查看正在运行的内核版本)
补充:内核打印
printk 和 printf比较:中s
- 相同点:都是打印信息
- 不同点:printk 在内核打印中使用,printf 在应用程序中使用; printk允许根据严重程度,通过附加优先级来对消息分类
在 linux/kernel.h 中定义了8种记录级别:按优先级的递减顺序分:
- KERN_EMERG “<0>” 用于紧急消息,通常是崩溃信息
- KERN_ALERT “<1>” 立刻行动的信息
- KERN_CRIT “<2>” 严重情况
- KERN_ERR “<3>” 错误情况
- KERN_WARNING “<4>” 有问题的警告
- KERN_NOTICE “<5>” 正常情况,但是仍然值得注意
- KERN_INFO “<6>” 信息型消息
- KERN_DEBUG “<7>” 用作调试信息
没有指定级别的话,默认 DEFAULT_MESSAGE_LOGLEVEL 4