Linux内核模块编程

本文详细介绍了Linux内核模块编程的特点、函数架构、编译方法和操作命令,包括insmod、rmmod等。讨论了内核模块的许可证、输出级别、参数使用和依赖。此外,还讲解了Linux系统调用的原理和实现过程,以及如何添加新的系统调用。最后,通过HelloWorld模块的编写,展示了内核模块的初始化、参数传递和文件分割等进阶技巧。
摘要由CSDN通过智能技术生成

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
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值