一、linux内核模块编程特点:
1.不能使用C库和C标准头文件
2.必须使用GNU规范
3.没有内存保护机制
4.不能处理浮点运算
5.注意同步和并发的问题
6.注意可移植性
二、加载函数和卸载函数格式
// 引入相关内核头文件
#include <linux/init.h>
#include <linux/module.h>
// 内核模块信息,包括许可证、作者、描述和版本等
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Leaf");
MODULE_DESCRIPTION("An hello worlk module for demonstration");
MODULE_VERSION("1.0");
int __init hello_init(void) //函数名任意定义,可以是main 也可以是是 init
{
printk("hello everyone!\n");
return 0;
}
void __exit hello_exit(void)
{
printk("bye bye!\n");
}
module_init(hello_init); //用宏来指定入口 加载模块时里面的加载函数会被调用
module_exit(hello_exit);
重点:要使用module_init和module_exit修饰,告诉内核模块的加载函数和卸载函数。
知识扩展:
__init宏: 告知编译器,将变量或函数放在一个特殊的区域,这个区域定义在vmlinux.lds中。__init将函数放在".init.text"这个代码区中.
__initdata宏: 将数据放在".init.data"这个数据区中。
标记为初始化的函数,表明该函数供在初始化期间使用。在模块装载之后,模块装载就会将初始化函数扔掉。这样可以将该函数占用的内存释放出来。
__exit宏: 告知编译器,将函数放在".exit.text"这个区域中。
__exitdata宏: 则告知编译器将数据放在".exit.data"这个区域中。
exit.*区域仅仅对于模块是有用的:如果编译稳定的话,exit函数将永远不会被调用。只有当模块支持无效的时候,exit.*区域将被丢弃。这就是为什么定义中会出现ifdef。
三、编译模块可以使用内核中编译模块的方法
模块的操作命令
insmod -> 加载模块,内核会执行模块加载函数
rmmod -> 卸载模块,内核会执行模块卸载函数
lsmod -> 查看当前已加载的模块
modinfo -> 查看模块信息
modprobe -> 加载模块,内核会执行模块加载函数
将模块加载到内核的方法:
insmod xxx.ko
lsmod xxx.ko
rmmod xxx.ko
modprobe和insmod的区别?
modprobe需要模块信息文件的支持modules.dep,modprobe还会检查模 块的依赖,自动加载依赖的模块,insmod则没有这些性质。
modinfo也需要modules.dep的支持。
四、模块许可证(GPL)
MODULE_LICENSE(“GPL”);
如果不加,内核提示警告信息,内核有些函数将无法使用。
MODULE_LICENSE("GPL"); //其实就是授权声明,为了维护版权
// "GPL" 是指明了 这是GNU General Public License的任意版本
// “GPL v2” 是指明 这仅声明为GPL的第二版本
// "GPL and addtional"
// "Dual BSD/GPL"
// "Dual MPL/GPL"
// "Proprietary" 私有的
// 除非你的模块显式地声明一个开源版本,否则内核会默认你这是一个私有的模块(Proprietary)。
MODULE_AUTHOR(" ") // 声明作者