Linux内核模块编程

  • Post author:
  • Post category:linux




1. Linux内核模块编程特点

  1. 不能使用C库和C标准头文件
  2. 必须使用GNU规范
  3. 没有内存保护机制
  4. 不能处理浮点运算
  5. 注意同步和并发问题
  6. 注意可移植性
  7. debug不能用gdb调试
  8. 标准输出是输出到文件,不是输出到屏幕
  9. 没有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 原理

  1. 应用程序会调用open
  2. 进程会调用C库中对应的open函数的实现
  3. C库的open实现会将open对应的系统调用号保存到寄存器中
  4. C库的open实现会调用软中断swi(svc)触发一个软中断异常
  5. 进程就会跳转到内核实现定义的一个位置(异常向量表
  6. 该位置对应的异常向量入口是(vector_swi)
  7. 这个函数会根据系统调用号,在系统预先定义的一个系统调用表中找到open函数对应的内核实现sys_open
  8. 执行该函数
  9. 执行完毕,原路返回



7.3 添加一个系统调用

  1. 添加系统调用的内核实现

    在arch/arm/kernel/sys_arm.c中添加新系统调用的内核实现sys_add;
  2. 添加新的系统调用号

    在arch/arm/include/asm/unistd.h中添加一个新的系统调用号__NR_add;
  3. 更新系统调用表

    在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



版权声明:本文为echojl原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。