从学习C语言开始,我们的第一个例子总是以hello world来说明,同样,学习Linux设备驱动开始从Helllo World开始!
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,Beautiful World!\n"); } module_init(hello_init); module_exit(hello_exit);
最下面的两个特殊宏module_init和module_exit用来告诉内核上述连个函数的作用,而另外一个特殊宏MODULE_LISENCE用来说明该模块采用自由许可证
printk在Linux的内核中定义,属于内核的打印输出函数,运行时不依赖于C库
模块之所以能够调用printk,是因为在insmod函数将模块装入内核后,模块就连接到了内核,从而可以访问内核的公用符号(函数和变量)
KERN_ALERT定义了这条打印信息的优先级(默认优先级的消息可能不会输出在控制台上)
接下来为了装载hello world模块,我们先为模块创建makefile,用来构造hello.ko模块
obj-m := hello.o
makefile此处不详细介绍,假如一个模块是由两个C文件生成的,测makefile如下
obj-m := module.ko
module-objs := file1.o file2.o
为了让makefile文件能正常工作,必须在大的内核构造系统环境中调用它们,如果内核的源代码树保存在
~/kernel-2.6目录中,则用来构造模块的make'命令是
make -C ~/kernel-2.6 M='pwd' modules
上述命令首先改变目录到-C指定的位置(内核的源代码树),其中保存有内核的顶层makefile文件, M=选项让该makefile
在构造modules目标之前返回到模块源代码目录,然后modules目标指向obj-m变量中设定的模块
以上这种方法比较麻烦,因此还有一种makefile方法
#如果已经定义KERNELRELEASE,则说明是从内核构造系统调用 因此可利用其内建语句
ifneq ($(KERNELRELEASE))
obj-m := hello.o
#否则,是直接从命令行调用的,这时要调用内核构造系统
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
当makefile从命令行调用时,它注意到KERNELRELEASE变量尚未设置,而已装载模块目录中存在一个符号
链接,它指向内核的构造树,从而定位到内核源代码目录,找到内核源代码树之后调用default:目标,这个目标使用第一种方法运行make命令,从而构造模块
Makefile编写文件如下:
ifneq ($(KERNELRELEASE),) obj-m := hello.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
加载模块 insmod ./hello.ko
卸载模块 rmmod hello
然后在 dmesg 即可看到模块加载和卸载时的打印信息了
模块的装载和卸载
insmod
装载 insmod 依赖于kernel/module.c中的一个系统调用----函数sys_init_module(系统调用通常都是带有sys_前缀的)给模块分配内核内存( 函数vmalloc负责内存分配)以便装载模块,然后该系统调用将模块正文复制到内存区域,并通过内核符号表解析模块中的内核引用,最后调用模块初始化函数
modprobe
同样也是将模块装载如内核,它于insmod的区别在于它会考虑要装载的模块是否引用了一些当前内核不存在的符号,如果有这类引用,modprobe会在当前模块搜索路径中查找定义了这些符号的其他模块,如果modprobe找到了这些模块(与要装载的模块所依赖的模块),它会同时将这些模块加载到内核,在这种情况下使用insmod,会失败,并在系统日志文件中(/var/log/messages)记录"unresolved symbols"的消息
rmmod
移除已装载入内核的模块
lsmod
通过读取/proc/modules虚拟文件来获取
已装载的模块可以在sysfs虚拟文件系统的/sys/module下找到
版本依赖问题,在缺少modversions的情况下,模块代码必须要针对链接的每个版本的内核重新编译