LINUX学习笔记21——内核模块

来源:岁月联盟 编辑:exp 时间:2012-02-29
(一)   LINUX“内核模块”的开发:
 
1.         如何使用需要的组件
 
a)         把所有的组件都编译进内核文件:
 
                                                     i.              缺陷1:内核文件过大
 
                                                   ii.              缺陷2:如果要添加或删除某个组件,需要重新编译整个内核
 
b)         使用“内核模块”的机制:
 
                                                     i.              模块本身并不被编译进内核文件
 
                                                   ii.              以根据需求,在内核运行期间动态的安装或卸载。
 
2.         程序结构:例:vi hello.c
 
a)         #include <linux/init.h>
 
b)         #include <linux/module.h>   头文件
 
c)         static int hello_init(void)
 
d)         {
 
e)         printk(KERN_WARNING "Hello, world !/n");   打印函数
 
f)          return 0;
 
g)         }
 
h)         static void hello_exit(void)
 
i)           {
 
j)           printk(KERN_INFO "Goodbye,  world/n");
 
k)         }
 
l)           module_init(hello_init);       模块加载函数(必需)
 
m)       module_exit(hello_exit);      模块卸载函数(必需)
 
3.         模块的编译:使用Makefile,基本格式都差不多,这里不能使用GCC
 
a)         编写单文件的Makefile:注意M大写
 
Ifneq ($(KERNELRELEASE),)     第1次变量为空,执行else;
 
 
 
obj-m := hello.o            目标名,需要根据情况修改
 
 
 
else
 
 
 
kDIR := /lib/modules/2.6.18-53.el5/build  内核源代码路径:根据情况修改
 
all:
 
make –C $(KDIR)  M=$(PWD)modules  –C进入目录,使用目录中的makefile来编译当前目录下的模块代码,再次进入该代码,执行if真
 
clean:
 
         rm –f *.ko *.o *.mod.o *.mod.c *.symvers
 
endif
 
                                                     i.              报错:missing separator  缺少分隔符:注意分隔符,格式是否正确
 
                                                   ii.              报错:没有找到Makefile文件,查看M是否大写,文件名写对没
 
b)         编写多文件的Makefile:需要改一个地方
 
                                                     i.              obj-m := hello.o
 
                                                   ii.              hello-objs := main.o add.o
 
4.         安装与卸载
 
a)         加载insmod :insmod hello.ko
 
b)         卸载rmmod :rmmod hello
 
c)         查看lsmod
 
d)         加载modprobe (modprobe hello): modprobe 如同insmod, 也是加载一个模块到内核。它的不同之处在于它会根据文件/lib/modules/<$version> /modules.dep来查看要加载的模块, 看它是否还依赖于其他模块,如果是,modprobe 会首先找到这些模块, 把它们先加载到内核。
 
5.         注意:应用程序是从头(main)到尾执行任务,执行结束后从内存中消失。内核模块则是先在内核中注册自己以便服务于将来的某个请求,然后它的初始化函数结束,此时模块仍然存在于内核中,直到卸载函数被调用,模块才从内核中消失。
 
6.         模块可选信息:
 
a)         许可证申明:宏MODULE_LICENSE用来告知内核, 该模块带有一个许可证,常用”GPL” :MODULE_LICENSE(”GPL”);
 
b)         作者申明(可选):MODULE_AUTHOR(“Simon Li");
 
c)         模块描述(可选):MODULE_DESCRIPTION("Hello World Module");
 
d)         模块版本(可选):MODULE_VERSION("V1.0");
 
e)         模块别名(可选):MODULE_ALIAS("a simple module");
 
f)          模块参数:模块参数用于在加载模块时传递参数给模块。
 
                                                     i.              module_param(name,type,perm):module_param(a,int, S_IRUGO);
 
                                                   ii.              name是模块参数的名称,
 
                                                  iii.              type是这个参数的类型:bool:布尔型int:整型charp:字符串型
 
                                                  iv.              perm是模块参数的访问权限。
 
1)         S_IRUGO:任何用户都对/sys/module中出现的该参数具有读权限
 
2)         S_IWUSR:允许root用户修改/sys/module中出现的该参数
 
                                                   v.              例:static int age=10;
 
                                                  vi.                  module_param(age,int, S_IRUGO);
 
7.         内核符号导出:
 
a)         作用:当多个模块有依赖时,需要把被依赖模块中用的的符号导出,这样其它的模块才能用它。
 
b)         /proc/kallsyms 记录了内核中所有导出的符号的名字与地址。
 
c)         方法:在文件末尾加上:EXPORT_SYMBOL(符号名)
 
d)         EXPORT_SYMBOL_GPL(符号名):其中EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块。
 
8.         常见问题:版本不匹配
 
a)         使用modprobe  --force-modversion 强行插入
 
b)         确保编译内核模块时,所依赖的内核代码版本等同于当前正在运行的内核。
 
c)         *可通过uname –r 察看当前运行的内核版本
 
9.         Printk与Printf对比
 
a)         Printk在内核中使用,Printf在应用程序中使用
 
b)         Printk允许根据严重程度,通过附加不同的“优先级”来对消息分类。按照优先级递减的顺序分别是:
 
                                                     i.              作用:控制在什么情况下打印,数字越小,级别越高
 
                                                   ii.              KERN_EMERG    “<0>”用于紧急消息,常常是那些崩溃前的消息。
 
                                                  iii.              KERN_ALERT    “<1>”需要立刻行动的消息。
 
                                                  iv.              KERN_CRIT       “<2>”严重情况。
 
                                                   v.              KERN_ERR       “<3>”错误情况。
 
                                                  vi.              KERN_WARNING   “<4>”有问题的警告
 
                                                vii.              KERN_NOTICE    “<5>”正常情况,但是仍然值得注意
 
                                               viii.              KERN_INFO      “<6>”信息型消息
 
                                                  ix.              KERN_DEBUG     “<7>”用作调试消息
 
                                                   x.              没有指定优先级的printk默认使用DEFAULT_MESSAGE_LOGLEVEL优先级,它是一个在kernel/printk.c中定义的整数。在2.6.29内核中#define DEFAULT_MESSAGE_LOGLEVEL 4
 
                                                  xi.              控制台优先级配置:文件/proc/sys/kernel/printk
 
文件中有4个数字:6 4 1 7  分别表示下面的优先级:
 
Console_loglevel     纯字符控制台优先级
 
Default_message_loglevel  默认消息优先级
 
Minimum_console_level
 
Default_console_loglevel
 
只有消息优先级高于控制台优先级时,才能把消息打印到控制台上
 
查找那些没有被打印出的语句:vi /var/log/messages