《Rice linux 学习开发》-linux 了解内核模块的原理
扫描二维码
随时随地手机看文章
如果将所有的设备驱动和内核功能都集成在内核中,则内核会不断的庞大,对我们的内核裁剪也会带来更大的挑战,为了解决这个问题,Linux内核引入内核模块机制,通过动态加载内核模块,从而实现在运行过程中扩展内核的功能。
内核模块是什么?
1 内核模块是一种没有经过链接,不能独立运行的目标文件,是在内核空间中运行的程序。经过链接装载到内核里面成为内核的一部分,可以访问内核的公用符号(函数和变量)。
2 内核模块可以让操作系统内核在需要时载入和执行,在不需要时由操作系统卸载。它们扩展了操作系统内核的功能却不需要重新启动系统。
3 如果没有内核模块,我们不得不一次又一次重新编译生成单内核操作系统的内核镜像来加入新的功能。这还意味着一个臃肿的内核。
模块机制的优点:
1 减小内核映像尺寸,增加系统灵活性;
2 节省开发时间;修改内核,不必重新编译整个内核。
3 模块的目标代码一旦被链入内核,作用和静态链接的内核目标代码完全等价。
模块机制的缺点:
1 对系统性能有一定损失;
2 使用不当时会导致系统崩溃;
接下来通过介绍一下内核模块的实现:
首先先附上模块的代码(hello_world.c)
1 #include
2 #include
3 #include
4
5 static int hello_init(void)
6 {
7 printk("hello world module! ");
8 return 0;
9 }
10
11 static void hello_exit(void)
12 {
13 printk("good bye module! ");
14 }
15
16 module_init(hello_init);
17 module_exit(hello_exit);
18
19 MODULE_LICENSE("GPL");
内核模块至少包含两个函数:
入口函数->初始化函数(xxx_init()):模块加载时,被调用
出口函数->卸载函数(xxx_exit()):模块卸载时,被调用
模块的入口函数名和出口函数名可以任意命名,通过宏module_init()申明入口函数,通过宏module_exit()申明出口函数。模块需要包含头文件:#include
内核模块证书:2.4内核后,引入识别代码是否在GPL许可下发布的机制 。在使用非公开的源代码产品时会得到警告。通过宏MODULE_LICENSE(“GPL”),设置模块遵守GPL证书,取消警告信息。
内核模块的构建:
管理模块源码方法: ① 模块源码加入到内核源码树中。② 模块源码饭仔内核源码树之外。
注:本文介绍的是放在内核源码树外编译。
接下来是Makefile的实现:
首先附上Makefile的源码
1 KERNEL_DIR = /home/FAN/linux-kernel
2
3 all:
4 make -C $(KERNEL_DIR) M=`pwd` modules
5 clean:
6 make -C $(KERNEL_DIR) M=`pwd` modules clean
7 rm -rf modules.order Module.symvers
8
9 obj-m += hello_world.o
其中:
1、KERNEL_DIR:参数,赋值内核的位置,例如我的内核源码的路径:/home/fan/linux-kernrl
2、当终端执行make时,会运行make -C $(KERNEL_DIR) M=`pwd` modules,其中M=`pwd`指向是模块所在的路径。
3、当终端执行make clean时,会运行下面两条命令,将编译生成的文件删除
make -C $(KERNEL_DIR) M=`pwd` modules clean
rm -rf modules.order Module.symvers
4、obj-m += hello_world.o:其中 –m 表示将hello_world.c编译成模块
–y 表示将hello_world.c编译进内核镜像中
编译生成文件:
在终端运行make,则会生成hello_world.ko文件.
将hello_world.ko发送到板子上,然后在板子上运行insmod hello_world.ko, 会调用hello_init()函数运行rmmod hello_world.ko,会调用hello_exit()函数
下图为运行结果: