模块的定义
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的模块通常由一组函数和数据结构组成,用来实现一种文件系统,一个驱动程序或其他内核上层的功能
我们在刚开始学习内核模块的时候,可以先编写一个简单的内核模块,对它进行编译,并链接到内核里面运行,通过这个过程可以大概了解模块的编写规则以及从编写到运行的过程。
下面是一个简单的内核模块的例子
源程序“hello.c”
/*包含了对模块的结构定义以及模块的版本控制,
* 任何模块程序的编写都要包含这个头文件*/
#include <linux/module.h>
#include <linux/kernel.h> //包含了常用的内核函数
/*宏__init告诉编译程序相关的函数和变量仅用于初始化,
* 编译程序将标有__init的所有代码存储到特殊的内存段中,
* 初始化结束后就释放这段内存*/
#include <linux/init.h> //包含了宏__init和宏__exit
static int __init lkp_init(void) //lkp_init()是模块初始化函数
{
/*printk()函数,该函数是由内核定义的,功能与C库中的
printf()类似,它把要打印的信息输出到终端或者系统日志*/
printk(KERN_EMERG"Hello,Kernel!\n");
printk(KERN_ALERT"Hello,Kernel!\n");
printk(KERN_CRIT"Hello,Kernel!\n");
printk(KERN_ERR"Hello,Kernel!\n");
printk(KERN_WARNING"Hello,Kernel!\n");
printk(KERN_NOTICE"Hello,Kernel!\n");
printk(KERN_INFO"Hello,Kernel!\n");
printk(KERN_DEBUG"Hello,Kernel!\n");
return 0;
}
static void __exit lkp_cleanup(void) //lkp_cleanup()是模块的退出和清理函数
{
printk("<1>Goodbye,World! leaving kernel space...\n");
}
/*是模块编程中最基本也是必需的两个函数*/
module_init(lkp_init); //向内核注册模块提供新功能
module_exit(lkp_cleanup); //注销由模块提供所有的功能
MODULE_LICENSE("GPL"); //告诉内核该模块具有GNU公共许可证
上面的模块初始化函数中我写了八个printk,这八个printk输出的内容是一样的,不同的是输出级别设定的不同,关于printk的八个输出级别参数在下次会单独讲,下面运行结果截图可以看出输出级别不同的运行结果。
Makefile文件
obj-m :=hello.o #产生hello模块的目标文件
all:
make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) modules #编译模块
clean:
make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) clean #清理
上面的Makefile中使用了obj-m := 这个赋值语句,其含义说明要使用目标文件hello.o建立一个模块,最后生成的模块名字是hello.ko
执行了make之后,当前目录下会生成加载内核模块所需要的文件
运行代码
将编译好的内核模块插入到内核中需要 insmod hello.ko这条命令
因为是要插入内核,要在内核空间运行,所以普通用户是不能直接把内核模块插入到内核中的,必须要获得权限才能进行内核模块的插入操作
普通用户没有权限
获得权限后执行插入命令,没有提示
执行玩命令后我们可以用lsmod查看我们编写的模块是否插入成功
可以看到第一个模块就是我们的写好的hello模块,表示已经插入成功了
接下来我们可以使用dmesg这条命令查看我们编写的模块执行情况
可以看到打印出来八条“Hello,Kernel!”因为输出级别设定的不同,所以打印出的效果也不一样。
最后我们要卸载内核模块时候,就要用到rmmod hello这条命令
删除之后,再用lsmod查看就会发现我们把hello模块卸载掉了
总结:
通过写一个简单的内核模块,能对内核模块的编写有一个初步的认识,知道了一个内核模块程序的基本结构,以及如何对内核模块进行编译,插入内核中,如何查看内核模块运行情况,其实对内核操作的命令也和普通的命令相似,比如ls查看文件lsmod查看加载的内核模块,rm删除文件,rmmod卸载已插入的内核模块等