🔥博客主页:PannLZ
🎋系列专栏:《Linux系统之路》
🥊不要让自己再留有遗憾,加油吧!
1模块的入点和出点
内核驱动程序都有入点和出点:前者对应于模块加载时调用的函数(modprobe和insmod),后者是模块卸载时执行的函数(在执行rmmod或modprobe -r时)。
main()函数是用C/C++编写的每个用户空间程序的入点,当这个函数返回时,程序将退出。而对于内核模块,情况就不一样了:入点可以随意命名,它也不像用户空间程序那样在main()返回时退出,其出点在另一个函数中定义。
唯一必须要做的是把它们作为参数提供给module_init()
和module_exit()
宏,将它们标识为相应的加载和删除函数。以此通知内核把哪些函数作为入点或出点来执行。
module_init()用于声明模块加载(使用insmod或modprobe)时应该调用的函数。初始化函数中要完成的操作是定义模块的行为。module_exit()用于声明模块卸载(使用rmmod)时应该调用的函数。
_init
和__exit
实际上是在include/linux/init.h
中定义的内核宏,如下所示:
#define __init__section(.init.text)
#define __exit__section(.exit.text)
__init关键字告诉链接器将该代码放在内核对象文件的专用部分。这部分事先为内核所知,它在模块加载和init函数执行后被释放。这仅适用于内置驱动程序,而不适用于可加载模块。内核在启动过程中第一次运行驱动程序的初始化函数。
由于驱动程序不能卸载,因此在下次重启之前不会再调用其init函数,没有必要在init函数内记录引用次数。对于_exit关键字也是如此在将模块静态编译到内核或未启用模块卸载功能时,其相应的代码会被忽略,因为在这两种情况下都不会调用exit函数。exit对可加载模块没有影响。
2模块信息
即使不读代码,也应该能够收集到关于给定模块的一些信息(如作者、参数描述、许可)。内核模块使用其.modinfo
部分来存储关于模块的信息,有MODULE_*
宏都用参数传递的值更新这部分的内容。
/*内核提供的在模块信息部分添加条目的真正底层宏是MODULE_INFO(tag,info),它添加的一般信息形式是tag=info。这意味着驱动程序作者可以自由添加其想要的任何形式信息*/
MODULE_INFO(my_field_name, "What eeasy value");
MODULE_LICENSE ("GPL"); /*定义模块的许可,没有MODULE_LICENSE()的模块被认为是非开源的,也会污染内核。模块至少必须与GPL兼容,才能享受完整的内核服务。*/
MODULE_AUTHOR() /*声明模块的作者*/
MODULE_DESCRIPTION() /*简要描述模块的功能:*/
#执行objdump -h module.ko即可打印出指定内核模块module.ko的不同组成部分。
#执行objdump module.ko -d -j .modinfo命令可以转储内核模块.modeinfo部分的内容