Linux内核模块基础代码解析

一.内核模块的定义

类似于浏览器、VScode的插件,Linux也提供了一种可以向正在运行的内核中插入新的代码段、在代码段不需要时也可以从内核中移除的机制,这个代码段称为内核模块。

优势:

  • 但内核扩展差的问题
  • 减少内核镜像文件体积,一定程度上节内存资源
  • 提高开发效率

注:不能彻底解决稳定性低的缺点,内核模块代码出错可能会导致整个系统崩溃。

二.内核模块的运行机制

  • 动态代码:是动态插入和移除的代码,但仍属于内核的一部分
  • 内存共享:内核模块与其它内核代码共享同一内存空间,存在形式上独立

三.内核源码解析示例

注:了解即可,小编也是copy的别人的代码~

#include <linux/module.h> //包含内核编程最常用的函数声明,如printk
#include <linux/kernel.h> //包含模块编程相关的宏定义,如:MODULE_LICENSE

/*该函数在模块被插入进内核时调用,主要作用为新功能做好预备工作
  被称为模块的入口函数
  
  __init的作用 : 
1. 一个宏,展开后为:__attribute__ ((__section__ (".init.text")))   实际是gcc的一个特殊链接标记
2. 指示链接器将该函数放置在 .init.text区段
3. 在模块插入时方便内核从ko文件指定位置读取入口函数的指令到特定内存位置
*/
int __init myhello_init(void)
{
    /*内核是裸机程序,不可以调用C库中printf函数来打印程序信息,
    Linux内核源码自身实现了一个用法与printf差不多的函数,命名为printk (k-kernel)
    printk不支持浮点数打印*/
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("myhello is running\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	return 0;
}

/*该函数在模块从内核中被移除时调用,主要作用做些init函数的反操作
  被称为模块的出口函数
  
  __exit的作用:
1.一个宏,展开后为:__attribute__ ((__section__ (".exit.text")))   实际也是gcc的一个特殊链接标记
2.指示链接器将该函数放置在 .exit.text区段
3.在模块插入时方便内核从ko文件指定位置读取出口函数的指令到另一个特定内存位置
*/
void __exit myhello_exit(void)
{
	printk("myhello will exit\n");
}

/*
MODULE_LICENSE(字符串常量);
字符串常量内容为源码的许可证协议 可以是"GPL" "GPL v2"  "GPL and additional rights"  "Dual BSD/GPL"  "Dual MIT/GPL" "Dual MPL/GPL"等, "GPL"最常用

其本质也是一个宏,宏体也是一个特殊链接标记,指示链接器在ko文件指定位置说明本模块源码遵循的许可证
在模块插入到内核时,内核会检查新模块的许可证是不是也遵循GPL协议,如果发现不遵循GPL,则在插入模块时打印抱怨信息:
	myhello:module license 'unspecified' taints kernel
	Disabling lock debugging due to kernel taint
也会导致新模块没法使用一些内核其它模块提供的高级功能
*/
MODULE_LICENSE("GPL");

/*
module_init 宏
1. 用法:module_init(模块入口函数名) 
2. 动态加载模块,对应函数被调用
3. 静态加载模块,内核启动过程中对应函数被调用
4. 对于静态加载的模块其本质是定义一个全局函数指针,并将其赋值为指定函数,链接时将地址放到特殊区段(.initcall段),方便系统初始化统一调用。
5. 对于动态加载的模块,由于内核模块的默认入口函数名是init_module,用该宏可以给对应模块入口函数起别名
*/
module_init(myhello_init);

/*
module_exit宏
1.用法:module_exit(模块出口函数名)
2.动态加载的模块在卸载时,对应函数被调用
3.静态加载的模块可以认为在系统退出时,对应函数被调用,实际上对应函数被忽略
4.对于静态加载的模块其本质是定义一个全局函数指针,并将其赋值为指定函数,链接时将地址放到特殊区段(.exitcall段),方便系统必要时统一调用,实际上该宏在静态加载时没有意义,因为静态编译的驱动无法卸载。
5.对于动态加载的模块,由于内核模块的默认出口函数名是cleanup_module,用该宏可以给对应模块出口函数起别名
*/
module_exit(myhello_exit);

总结:

内核模块的三要素

  • 三要素:入口函数、出口函数和MODULE_LICENSE宏是内核模块的三个基本要素。
  • 代码位置MODULE_INITMODULE_EXIT宏应放在对应函数定义的下方,MODULE_LICENSE宏位置不限。

开发建议

  • 固定模板:内核模块的代码结构可以作为一个固定模板,开发者只需修改函数名即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值