驱动程序的基本框架
头文件
就像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件:
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> |
init.h 定义了驱动的初始化和退出相关的函数,
kernel.h 定义了经常用到的函数原型及宏定义
module.h 定义了内核模块相关的函数、变量及宏。
初始化
任何一个驱动都去需要提供一个初始化函数,当驱动加载到内核中时,这个初始化函数就会被自动执行,初始化的函数原型定义如下:
typedef int (*initcall_t)(void); |
驱动程序是通过module_init宏来声明初始化函数的:
static int __init hello_init(void) { printk(KERN_ALERT "Hello World!\n"); return 0; } module_init(hello_init); |
__init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到(.init.text)段,这样当函数初始化完成后这个区域可以被清除掉以节约系统内存。Kenrel启动时看到的消息“Freeing unused kernel memory: xxxk freed”同它有关。
初始化函数是有返回值的,只有在初始化成功是才返回0,否则返回错误码(errno)。
卸载
如果驱动程序编译成模块(动态加载)模式,那么它需要一个清理函数。当移除一个内核模块时这个函数被调用执行清理工作。清理函数的函数原型定义为:
typedef void (*exitcall_t)(void); |
驱动程序是通过module_exit宏来声明清理函数的:
static void __exit hello_exit(void) { printk(KERN_ALERT "Goodbye World!\n"); } module_exit(hello_exit); |
同__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数,因为编译进内核的模块不需要做清理工作。显然,__init和__exit对动态加载的模块是无效的。
版权信息
Linux内核是按照GPL发布的,同样Linux的驱动程序也要提供版权信息,否则当加载到内核中是系统会给出警告信息。Hello World例子中的版权信息是GPL。
MODULE_LICENSE("GPL"); |
后续
这里你了解了一个驱动程序的基本框架,所有的驱动都会包含这些内容。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~