从第二章构造和运行模块开始,因为第一章我已经了解。
先贴第一个简单模块:
1 #include <linux/init.h>
2 #include <linux/module.h> //所有模块必须包含这两个头文件
3
4 MODULE_LICENSE("Dual BSD/GPL"); //许可证,另可以添加MODULE_AUTHOR MODULE_DESCRIPTION 等。具体规范化,可参考各发行的驱动源码。
5
6 static int hello_init(void)
7 {
8 printk(KERN_ALERT "hello world\n");
9 return 0;
10 }
11 static void hello_exit(void)
12 {
13 printk(KERN_ALERT "Goodbye, cruel world\n");
14 }
15
16 module_init(hello_init);
17 module_exit(hello_exit);
重点关注编译和装载 Makefile
1 obj-m:=hello.o
2 #KERNELDIR:=/home/hya/linux-3.2/
3 KERNELDIR:=/lib/modules/$(shell uname -r)/build/
4
5 hello:
6 make -C $(KERNELDIR) M=`pwd`
7 clean:
8 rm -rf *.mod.c *.o *.ko modules.order Module.symvers
9
模块的安装需要基于特定的内核平台,Makefile要找正确的KERNELDIR路径进行编译,KERNELDIR路径下的内核源码必须正确配置和构造了内核树。
首先我通过第二行指定内核路径,并且在该目录下正确 make menuconfig 、make。
在当前Ubuntu平台上无法insmod,报错如下:(注,insmod 必须是超级用户权限)
版本依赖问题
如果要在Makefile中同时编译多个module:
如果你写成 obj-m:=1.o
obj-m:=2.o
obj-m:=3.o
这样只会编译3.o,而忽略前面的两个;正确的做法应该是:
1 obj-m:=01_reg_chrdev.o
2 obj-m +=02_reg_cdev.o
3 obj-m +=03_scull_dev.o
(多看、多做、多想)
1、模块的初始化和关闭:
初始化:我所理解的初始化时相当于应用程序的main函数;
固定格式:
static int __init init_fun(void)
{
/*初始化代码*/
}
module_init(inti_fun);
__init:对内核来讲,是一种暗示,表明该函数仅在初始化期间使用,在模块被装载之后,模块装载器就会将初始化函数扔掉,将该函数占用的内存释放出来。
关闭:模块rmmod时调用,注销接口,并向系统返回所有资源。
固定格式:
static void __exit cleanup_fun(void)
{
/* 清除代码*/
}
module_exit(cleanup_fun);
如果一个模块未定义清除函数,则内核不允许卸载该模块。
2、
错误处理:
在内核注册设施时,要时间铭记注册可能会失败。模块初始化出现错误后,必须自行注销已注册的设施。如果由于某种原因未能撤销已注册的设备,内核会处于一种不稳定的状态。这就用到了goto语句,驱动似乎很喜欢使用goto,进行回退。以后代码看多了,慢慢就理解了。
3、模块参数:
sudo insmod hello_param.ko howmany=2 whom="Mom" 注意必须添加 “变量=”
源码:
6 static char *whom = "world";
7 static int howmany = 1;
8
9 module_param(howmany, int, S_IRUGO | S_IWUSR);
10 module_param(whom, charp, S_IRUGO | S_IWUSR);
。。。
16 printk(KERN_INFO "hello %d %s\n", howmany, whom);
。。。
暂时了解,相信以后实际使用还需仔细琢磨。
4、本章还简单介绍了一线内容:
核心模块和应用程序的对比,比如printk不支持浮点数等;
用户空间和内核空间的区别;
内核中的并发;当前进程task_struct;
内核符号表:EXPORT_SYMBOL(name);
模块装载竞争;
在用户空间编写驱动程序。
这些内容可能涉及到对于操作系统和Linux内核的理解,渐渐深入吧。