Kernel Module实战指南(一):Hello World!

Introduction

对于习惯了平时在Linux上进行C应用程序开发的你,是否想窥探一下底层的世界?
对于对Linux Kernel源码无从下手的你,是否希望有一个简易的方法?
是的,这里有一个地方,可以让你轻松的编写和调试Linux Kernel Code。
这个就是我将要介绍的Linux Kernel Module,即Linux内核模块。

Linux Kernel Module究竟是什么?

Linux Kernel Module是一段可以在运行时被加载到Linux Kernel中的代码,可以使用Kernel Functions。Linux Kernel Module的用途很广,最常见的例子就是Device Driver,也就是设备驱动程序。
如果没有Linux Kernel Module,每一行修改Kernel代码,每一个新增的Kernel功能特性,都需要重新编译Kernel,大大浪费了时间和效率。

如何查看已经加载的Linux Kernel Module

在bash下输入:

$ lsmod
Module      Size    Used by
nls_utf8   16384    1
isofs      30960    1
...

或者直接在bash下查看:

$ cat /proc/modules

Hello World!

闲话少说,开始编写我们的第一个Kernel Module,直接上代码,hello-world.c:

1
2
3
4
5
6
7
8
9
10
#include <linux/kernel.h>
#include <linux/module.h>

int init_module(void) {
printk(KERN_INFO "Hello World!\n");
return 0;
}
void cleanup_module(void) {
printk(KERN_INFO "Bye World!\n");
}

内核中打印函数和应用打印函数略微不同,printf()是glibc的函数,内核中使用printk()。实际上printk()的设计并不是为了打印,而是为了记录,根据配置不同,内容可能由syslogd或者klogd打印到/var/log/messages中,或者打印到你的console中。
编译的话,和应用层代码使用的gcc或者g++不同,Kernel Module使用Makefile,或者kbuild。下面是Makefile文件:

1
2
3
4
5
obj-m += hello-world.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

在命令行输入:

$ Make

如果成功,你将在当前目录看到hello-world.ko,以及其他的临时文件。
通过modinfo来查看module信息,在命令行输入:

$ modinfo hello-world.ko
filename: hello-world.ko
...

现在没有什么信息,别着急,后面会介绍如何将信息(author, license等)写到Kernel Module中。
运行我们的Kernel Module,在命令行输入:

$ insmod hello-world.ko

如果提示权限不够,输入:

$ sudo insmod hello-world.ko

执行完毕后,可能在console上不会显示任何结果。还记得前面提到的吗?我们需要查看/var/log/messages,最简单的方法是在命令行输入:

$ dmesg
...
[26484.975093] Hello World!

好了,我们卸载模块,同样在命令行输入:

$ rmmod hello-world.ko

如果提示权限不够,输入:

$ sudo rmmod hello-world.ko

执行完毕后,可能在console上不会显示任何结果。同样查看/var/log/messages:

$ dmesg
...
[26484.975093] Hello World!
[26739.017345] Bye World!

一个像样的Kernel Module

我们先简单包装一下Hello World的Code,让他看上去稍微丰富一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

static int __init init_my_module(void) {
printk(KERN_INFO "Hello, my module!\n");
return 0;
}

static void __exit exit_my_module(void) {
printk(KERN_INFO "Bye, my module!\n");
}

module_init(init_my_module);
module_exit(exit_my_module);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("csprojectedu");

这里我们使用了linux/init.h中提供的宏init和exit,解放我们的函数名,不再沿用蹩脚的init_module()和cleanup_module()了。
Makefile还是沿用之前的:

1
2
3
4
5
obj-m += my_module.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

在命令行输入:

$ Make

通过modinfo来查看module信息,在命令行输入:

$ modinfo my_module.ko
filename: my_module.ko
author: csprojectedu
license: GPL
...

是不是多了author和license?insmod的时候也不再报缺少license的警告了吧。

Summary

通过最简单的hello-world.c和Makefile(真的没有比这个更简单的代码了),介绍了如何编写我们的第一个Kernel Module。动手做一做吧,你已经迈向了内核世界的第一步。

(本文出自csprojectedu.com,转载请注明出处)