开发一个简单的内核程序
前面文章我们介绍了如何编译一个内核,也了解到了编译内核并不难。今天这篇文章我们将介绍一下如何开发一个简单的内核程序。
实例源程序
用于态的程序类似,Linux的内核程序也是有规范的。用户态的程序必须以main函数作为入口,而内核程序则必须包含一个加载函数和一个卸载函数。这是因为内核程序是一个内核模块,加载函数是模块加载的时候执行,而卸载函数则是在模块卸载的时候执行。
模块的加载函数和卸载函数需要通过内核API进行注册,也就是module_init和module_exit函数,也就是19行,20行的内容。
头文件自然不必多说,这个与用户态程序类似,必须要包含必要的头文件(2-4行)。另外一个必要的是第6行的License字段,这个也是必须的。

这里实现的功能是在模块加载和卸载的时候打印“Hello World”字符串。打印字符串使用的是printk函数,该函数与用户态的printf函数类似。需要注意的是字符串并不会打印到终端,而是打印到系统日志里面,可以通过dmesg命令查看。
Makefile文件
内核程序需要一个配套的Makefile文件,文件名称必须为Makefile,第一个字母大写。该文件的格式如下所示。其中第一行定义了模块的名称,然后接下来分别是当前路径,内核版本和内核源代码路径。

在这个Makefile文件中有两个目标,一个是编译目标all,用于生成二进制文件;另外一个则是清理目标,用于清理临时文件。
编译与运行
程序的编译非常简单,在当前目录执行make命令即可:
make
执行完命令后, 可以看到目录下出现了很多文件,其中hello.ko是生成的二进制文件,类似Windows下的exe文件。

如何执行该二进制文件呢?很简单,执行如下命令就行:
insmod hello.ko
执行成功后,通过lsmod可以看到该内核模块被加载了。

此时通过dmesg命令可以看到打印的内容。至此,我们完成了一个内核程序的开发,编译和运行。是不是很简单!
附录
最后,我们附上上述程序的源代码。下面是程序的源代码。
//必要的头文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.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 "Hello World\n");
}
//模块的注册
module_init(hello_init);
module_exit(hello_exit);
//声明模块的作者(可选)
MODULE_AUTHOR("SunnyZhang");
//声明模块的描述(可选)
MODULE_DESCRIPTION("This is an example!/n");
//声明模块的别名(可选)
MODULE_ALIAS("A simplest example");
下面是Makefile文件的源代码。
obj-m += hello.o
#generate the path
CURRENT_PATH:=$(shell pwd)
#the current kernel version number
LINUX_KERNEL:=$(shell uname -r)
#the absolute path
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
#complie object
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
#clean
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean