1. Linux内核模块编程特点
- 不能使用C库和C标准头文件
- 必须使用GNU规范
- 没有内存保护机制
- 不能处理浮点运算
- 注意同步和并发问题
- 注意可移植性
- debug不能用gdb调试
- 标准输出是输出到文件,不是输出到屏幕
- 没有main函数,只有初始化函数和一个提出函数
2. 函数架构
int xxx(){
return 0;//成功
return 负值;//失败
}
/*使用module_init()函数告诉内核模块的加载函数
* 使用module_exit()函数告诉内核模块的卸载函数
* /
module_init(xxx);
module_exit(yyy);
这两个函数必须要有。不过可以重写。
3.编译模块可以使用内核中编译模块的方法
3.1 模块的操作命令
**insmod:**加载模块,内核会执行模块加载函数;
**rmmod:**卸载模块,内核会执行模块卸载函数;
**lsmod:**查看当前已加载的模块;
**modinfo:**查看模块信息;
**modprobe:**加载模块,内核会执行模块加载函数;
modprobe和insmod的区别
modprobe需要模块信息文件的支持modules.dep,modprobe还会检查模块的依赖,自动加载依赖的模块,insmod则没有这些性质。
modinfo也需要modules_dep的支持。
3.2 模块许可证(GPL)
MODULE_LICENSE("GPL v2");
如果不加,内核提示警告信息,内核有些函数将无法使用。
3.3 内核的输出级别和printk的输出级别
**printk函数具有打印级别,0-7,数字越小,级别越高。**内核也有默认的输出级别,当printk的级别小于内核级别,printk打印内容才可以被打印。
cat /proc/sys/kernel/printk
可以查看内核输出级别
4. 什么是内核模块
内核模块是具有独立功能的程序,它可以被编译,但是不能单独运行,它的运行必须被链接到内核作为内核的一部分在内核空间中运行。
5. 内核模块参数
5.1 作用
在加载模块和加载模块后,能够给模块传递相应的参数信息
5.2 使用
module_param();
module_param_array();
//在其他文件调用时候,extern申明下载就可以了,但是加载insmod的时候,必须先加载所依赖的模块,再加载自己
**/sys/module/模块名/paramters/**目录下有对应的文件。(需要模块参数的权限不为0)
我们可以通过修改这些文件的内容来实现对模块参数的修改。
6. 内核模块依赖
一个模块使用了另一个模块的变量或者函数,第一个模块就依赖于第二个模块。
6.1 模块的导出符号
如果一个模块中的变量或者函数希望被别的模块使用,需要将对应的变量或者函数导出
EXPORT_SYMBOL();
EXPORT_SYMBOL_GPL();
前一个导出内容都可以用,后一个只有遵循GPL协议的才能用,模块编程一定要添加
MODULE_LICENSE(“GPL v2”);
7. Linux系统调用原理和实现
7.1 作用
为用户统一提供硬件抽象层,操作一个文件,无需关心这个文件是存在于哪个硬件上,只需要调用对应的系统调用即可(open read write...)。
7.2 原理
- 应用程序会调用open
- 进程会调用C库中对应的open函数的实现
- C库的open实现会将open对应的系统调用号保存到寄存器中
- C库的open实现会调用软中断swi(svc)触发一个软中断异常
- 进程就会跳转到内核实现定义的一个位置(异常向量表
- 该位置对应的异常向量入口是(vector_swi)
- 这个函数会根据系统调用号,在系统预先定义的一个系统调用表中找到open函数对应的内核实现sys_open
- 执行该函数
- 执行完毕,原路返回
7.3 添加一个系统调用
-
添加系统调用的内核实现
在arch/arm/kernel/sys_arm.c中添加新系统调用的内核实现sys_add; -
添加新的系统调用号
在arch/arm/include/asm/unistd.h中添加一个新的系统调用号__NR_add; -
更新系统调用表
在arch/arm/kernel/calls.S中的系统调用表中添加新的一项:CALL(sys_add);
重新编译内核,新内核就有新的系统调用,可以在用户空间使用syscall函数完成对新的系统调用的调用。
8. 开始简单模块的编写(HelloWorld)
8.1 最简单的内核模块
#include<linux/module.h>//所有模块必须包含
#include<linux/kernel.h>//一些宏定义,例如这里的KERN_INFO
int init_module(void){
printk(KERN_INFO