内核模块是Linux内核向外部提供的一个接口,是内核的一部分,但没有被编译到内核里面去
linux内核之所以提供模块机制,是因为它本身是一个单内核。而单内核的最大优点就是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷
什么是模块,模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或者其他内核上层的功能。
编写简单的内核模块
一个内核模块至少需要两个函数
1.module_init() 模块加载函数
当模块被插入内核时调用它
2.module_exit() 模块卸载函数
当模块从内核移走时调用它
任何模块都要包含的三个头文件:
#include <linux/module.h>
#include <linux/kernel.h>
#incldue <linux/init.h>
module.h头文件包含了对模块的版本控制;
kernel.h包含了常用的内核函数;
init.h包含了宏__init和__exit,宏__init告诉编译程序相关的函数和变量仅用于初始化,编译程序将标有__init的所有代码存储到特殊的内存段中,初始化结束就释放这段内存。
注:printk()函数
该函数是由内核定义的,功能和C库中的printf()类似,它把要打印的日志输
出到终端或系统日志。字符串中的<1>是输出的级别,表示立即在终端输出。
内核模块的入口函数和出口函数
内核模块的入口函数和出口函数是模块编程中最基本的也是必须的两个函数。入口函数向内核注册模块所提供的新功能;出口函数负责注销所有由模块注册的功能。例如;
Static int _init lkm_init(void)
{
printk(KERN_INFO “Hello World!\n”);
return 0;
}
Static void _exit lkm_exit(void)
{
printk(KERN_INFO “Goodbye!\n”);
}
加载模块、卸载模块和声明许可证
module_init和module_exit,这两个函数分别在加载和卸载模块时被调用。
module_init(lkm_init);
module_exit(lkm_exit);
声明许可证:
MODULE_LICENSE(“GPL”);
以上是hello world的.c文件编写格式
内核模块的Makefile文件
内核模块不是独立的可执行文件,但在运行时其目标文件被链接到内核中。只有超级用户才能加载和卸载模块。
Makefile文件的基本内容如下:
1.obj-m:=module_example.o
#产生的module_example模块的目标文件
2.CURRENT_PATH := $(shell pwd)
#模块所在的当前路径
3.LINUX_KERNEL :=$(shell uname -r)
#linux内核源代码的当前版——本
4.LINUX_KERNEL_PATH := /user/src/linux-headers-¥(LIUNX_KERNEL)
#linux内核源代码的绝对路径
5.all:
6. make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
#编译模块
7.claen:
8. make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
#清理模块
obj-m :=这个赋值语句的含义是说明要使用目标文件module_example.o建立一个模块,最后生成的模块名为module_example.ko。.o文件是经过编译和汇编,而没有经过链接的中间文件。
注:makefile文件中,若某一行是命令,则它必须以一个Tab键开头。
装载模块
当编译好模块,就可以用insmod命令将新的模块插入到内核中,如:
insmod module_example.ko
然后,可以用lsmod命令查看模块是否正确地插入到了内核中。模块的输出由printk()产生。该函数默认打印系统文件/var/log/messages的内容。
卸载模块
使用rmmod命令加上在insmod中看到的模块名,就可以从内核中移除该模块:
rmmod module_example