Linux提供了一种 需要时可以被动态加载和移除的代码的机制,这种机制称为模块(Module),内核模块具有以下两个特点:
内核模块的本质:一段隶属于内核的“动态”代码,与其它内核代码是同一个运行实体,共用同一套运行资源,只是存在形式上是独立的。
记住,内核模块的运行是在内核空间里的。
#include
#include
/***************************************/
static int __init my_init(void){
printk("Hello Driver initalized!\n")
return 0;
}
static void __exit my_exit(void){
printk("Hello Driver exit!\n")
}
module_init(my_init);
module_exit(my_exit);
/***************************************/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Micher Lee");
MODULE_DESCRIPTION("");
MODULE_ALIAS("");
这是一个典型的内核模块,用注释符分隔成了三个部分。
而实际,只要记住模块三要素:入口函数 、出口函数、 MODULE__LICENSE
#include
#include
int __init my_init(void){};
void __exit my_exit(void){};
1.__exit是一个宏,展开后为:attribute ((section (“.exit.text”))) 实际也是gcc的一个特殊链接标记
2.指示链接器将该函数放置在 .exit.text区段
3.在模块插入时方便内核从ko文件指定位置读取出口函数的指令到另一个特定内存位置
MODULE_LICENSE(“GPL”);
字符串常量内容为源码的许可证协议 可以是"GPL" “GPL v2” “GPL and additional rights” “Dual BSD/GPL” “Dual MIT/GPL” "Dual MPL/GPL"等, "GPL"最常用
其本质也是一个宏,宏体也是一个特殊链接标记,指示链接器在ko文件指定位置说明本模块源码遵循的许可证
module_init(my_init);
module_exit(myhello_exit);
1.用法:module_exit(模块出口函数名)
2.对于静态加载的模块其本质是定义一个全局函数指针,并将其赋值为指定函数,链接时将地址放到特殊区段(.exitcall段),方便系统必要时统一调用,实际上该宏在静态加载时没有意义,因为静态编译的驱动无法卸载。
3.对于动态加载的模块,由于内核模块的默认出口函数名是cleanup_module,用该宏可以给对应模块出口函数起别名
MODULE_AUTHOR(字符串常量); //字符串常量内容为模块作者说明
MODULE_DESCRIPTION(字符串常量); //字符串常量内容为模块功能说明
MODULE_ALIAS(字符串常量); //字符串常量内容为模块别名
这些宏用来描述一些当前模块的信息,可选宏
这些宏的本质是定义static字符数组用于存放指定字符串内容,这些字符串内容链接时存放在.modinfo字段,可以用modinfo命令来查看这些模块信息,用法:modinfo 模块文件名
perm为对应文件 /sys/module/对应模块名/parameters/变量名的操作权限。 一般赋值0664即可。
关于perm这里多解释一下:模块被加载后,在/sys/module目录下会出现以此模块名命名的目录。当perm不为0时,在此目录下还将出现parameters目录,基中包含一系列以参数名命名的文件节点。这些文件的权限值就是perm的值。
/*模块名:book 编译后文件名为 book.ko */
#include
#include
static char *book_name = "hello";
module_param(book_name , charp , 0664);
static int book_um = 400;
module_param(book_num , int , 0664);
static init __init my_init(void){
printk("book name:%s\n", book_name);
printk("book num :%d\n", book_num);
return 0
}
static void __exit my_exit(void){
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
模块是内核的一部分,与内核是一个整体,因此就可以与内核其它程序去共享全局的符号。
Linux的“/proc/kallsyms”文件,对应着整个内核所有的符号表,它记录了符号以及符号所在在内存地址。
对于一个模块,可以通过如下的宏,将自身的符号导出到内核符号表中,可供其它内核进程使用。
本篇结束