kernel 模块与简单 hello 模块

Kernel 模块与简单 hello 模块

kernel 模块的简介

Linux 内核进行扩展时,例如编写驱动程序、netfilter功能等,最方便的方式是通过编写模块,然后加载到内核中。由于 kernel 模块已加载到内核,因此如果模块出现错误,将导致内核出错甚至系统崩溃。所以,一般建议在测试内核模块时,如果与设备无关的模块(非驱动程序等),最好是在虚拟机中进行。

大多数 Linux 发行版本都没有把 Linux 编译成一个整个文件,而是把非核心的子系统,如驱动程序等,编译成 kernel 模块,并在启动时加载。模块的目录一般是 /lib/modules/<内核版本>/ 目录下。内核版本号通过 $(uname -r) 获取。

加载内核模块时,可通过 modprobe (从默认路径搜索并加载) 或 insmod (指定模块路径)

# 加载 intel wifi 驱动
sudo modprobe iwlwifi
# 或
sudo insmod "/lib/modules/$(uname -r)/kernel/drivers/net/wireless/iwlwifi/iwlwifi.ko"

用 lsmod 命令查看已加载的模块

lsmod

而从内核中把模块删除则通过 rmmod 命令

sudo rmmod iwlwifi

编写 hello 示例模块

内核模块也是通过 C 编写和编译的本机代码,只是由于它需要被 Linux 模块加载框架来载入,因此需要特殊的模块注册处理才能生效。内核模块能直接访问内核的功能,比一般的 C 编程要更加小心,预防出现 BUG 和安全问题。

编写内核模块时,使用的头文件为 /lib/modules/$(uname -r)/build/include (通常 Linux 头文件在安装 kernel-image 同时安装,在一些发行版上 build 链接到 /usr/src/linux-headers-$(uname-r)/)。

先看看一个简单的 hello 模块

/*
* hello.c
* 简单的 hello 内核模块
*/ #include <linux/module.h> /* 所有模块使用 module.h */
#include <linux/kernel.h> /* 包含内核常用的函数声明等 */
#include <linux/init.h> /* 进行内存初始化和清理 */ MODULE_AUTHOR("fengyc");
MODULE_DESCRIPTION("This is a demo.");
MODULE_VERSION("0.0.1");
MODULE_LICENSE("GPL"); static int __init hello_init(void)
{
printk(KERN_INFO "Hello, world!\n");
return 0;
} static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, world!\n");
} module_init(hello_init);
module_exit(hello_exit);

所有的模块都需要到 module.h 头文件,使用 module_init() module_exit() 函数来注册模块入口和退出处理。这里的代码逻辑很简单,就是在模块加载时,打印 Hello, world! ,以及在退出时打印 Goodbye, world! (由于代码运行在内核空间里面,不能直接使用用户空间的 print 函数,而要使用内核中的 printk 函数)

编译时通过一个 Makefile 文件进行,把这个 Makefile 文件置于 hello.c 同一目录下(Makefile 中使用 tab 作为分隔符

obj-m += hello.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.ko。现在可通过 modinfo 命令查看模块信息

modinfo hello.ko

然后通过 insmod / rmmod 命令加载、卸载模块,并用 dmesg 查看内核的环形缓存区( kernel ring buffer )的信息( printk 输出到了这里)

sudo insmod hello.ko
sudo rmmod hello
dmesg

如果一切顺利,就可以看到 hello.ko 模块的输出。而有个这个基本的代码框架,就可以继续深入,使用 netlink 作为接口,进行用户空间与内核空间的通信,并调用内核的功能,实现在用户态无法完成的动作。

p.s. 为了更好地使用 GUI 的协助,可使用参考使用 eclipse 内核开发环境 http://blog.chinaunix.net/uid-24512513-id-3183457.html

上一篇:经典的SQL面试题


下一篇:nodejs的require模块及路径