本文共 2938 字,大约阅读时间需要 9 分钟。
其全称为动态可加载内核模块。之所以提供模块机制,是因为Linux是“单块内核”的操作系统,单内核的最大优点是效率高,因为所有的内容都集中在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。Linux内核是模块化组成的,它允许我们方便地在运行时动态地向内核中插入或从中删除代码。
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时加载,被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。
装入的内核模块和其他内核部分完全一样,具有相同的访问权限,因此,差的内核模块会导致系统崩溃;模块编程和内核版本密切相连,内核版本和模块版本的不兼容,也可能导致系统崩溃;这些代码被一并组合在一个单独的二进制镜像中,这样基本内核镜像就尽可能的小,而且灵活性很好否则,在需要添加新硬件或者升级设备驱动时,必须重新构建内核。
选项 | 应用程序 | 内核模块程序 |
---|---|---|
使用函数 | libc 库 | 内核函数 |
运行空间 | 用户空间 | 内核空间 |
运行权限 | 普通用户 | 超级用户 |
入口函数 | libc 库 | 内核函数 |
运行空间 | main() | module_init |
出口函数 | exit() | module_exit |
编译 | gcc | makefile |
链接 | gcc | insmod |
运行 | 直接运行 | insmod |
入口函数 | libc 库 | 内核函数 |
调试 | gdb | kdbug 、 kdb 、 kgdb |
还是从最经典的hello world写起。
/************************************************************************* > File Name: hello.c > Author: Tanswer_ > Mail: 98duxm@gmail.com > Created Time: 2017年02月27日 星期一 10时53分58秒 ************************************************************************/#include#include #include MODULE_LICENSE("Dual BSD/GPL");static int hello_init(void){ printk("hello world\n"); return 0;}static void hello_exit(void){ printk("Goodbye world\n");}module_init(hello_init);module_exit(hello_exit);
语法和我们平时写的C代码差异比较大,我也还没有适应,有机会再继续写。必要的两个函数:加载函数和卸载函数,分别还有另一种写法。printk是内核态下的打印函数。还可以加模块的声明与描述,比如:MODULE_AUTHOR("Tanswer");//作者
。我们详细看一下头文件,在编译前需要配一下环境。
#include所有模块都要使用的 #include 包含了常用的内核函数 #include 包含了宏_init和_exit,它们允许释放内核占用的内存内核头文件的位置:/usr/src/kernels/用户层头文件的位置:/usr/include
我进入/usr/src/后发现里面是空的,并没有源码树,所以执行 #yum install kernel-devel
安装源码树。安装完成后,我发现我的uname -r
和 源码树目录显示的内核版本号不一致
这就导致了我写的Makefile出现了问题,见下面代码。
obj-m := hello.oCURRENT_PATH:=$(shell pwd)KERNEL_NUM:= $(shell uname -r)#KERNEL_PATH:=/usr/src/kernels/$(KERNEL_NUM)KERNEL_PATH:=/usr/src/kernels/3.10.0-514.6.2.el7.x86_64all:# make -C $(KERNEL_PATH) M=$(CURRENT_DIR) modules make -C $(KERNEL_PATH) M=$(CURRENT_PATH) modulesclean: make -C $(KERNEL_PATH) M=$(CURRENT_PATH) clean
第一次我写的是注释掉的那句,结果make 的时候显示路径名不正确,上面的原因我还不知道,懂得请赐教。所以我直接将路径名写上去了,就是现在的那句。我再次make,看下图:
又出现了两个错误,我逐个解决,上面那个错误,提示说please install libelf-dev or elfutils-libelf-devel
那我就乖乖听话装上它,然后我再次make:
上面那个错误已经解决,剩下这个以前用makefile的时候也没遇到过,我google了一下,发现有提这个问题的,但是没有解决方法。我没办法,有点失望就随手点开Makefile来看,然后……我就发现了一个小错误。。。Makefile代码中 -C 后指定的是 Linux 内核源代码的目录,而 M= 后指定的是 hello.c 和 Makefile 所在的目录,但是我第一次写成了CURRENT_DIR,就是我注释掉的那句,可明明我定义的变量是CURRENT_PATH,然后我就改过来了。再次make,终于终于终于
编译正确。
补充(转):
.ko 是kernel object 的缩写,是Linux 2.6内核使用的动态连接文件,在Linux系统启动时加载内核模块1.insmod hello.ko 加载模块2.dmesg 查看结果3.rmmod 卸载模块
补充:常用的模块编程命令
1.lsmod命令查看系统中加载了的所有模块以及模块间的依赖关系
2.cat /proc/modules来查看加载模块信息 3.内核中已加载模块的信息也存在于 /sys/module 目录下,加载 hello.ko 后,内核中将包含 /sys/module/hello 目录,该目录下又包含一个 refcnt 文件和一个 sections 目录,在 /sys/module/hello 目录下运行 tree -a 可以看到他们之间的关系 4.使用 modinfo < 模块名 > 命令可以获得模块的信息,包括模块的作者,模块的说明,某块所支持的参数以及 vermagic