作者:jafon.tian
转载请注明出处:https://blog.csdn.net/JT_Notes
写过linux驱动程序的人都知道需要将驱动的初始化函数通过module_init注册,然后在通过menuconfig配置的时候选择随内核一起编译(非模块),系统在启动的时候就能够自动调用驱动初始化函数了。真是一件神奇的事情!
驱动程序模板(模板来源:https://blog.csdn.net/zhuhuibeishadiao/article/details/51407438)
#include <linux/kernel.h>
#include <linux/module.h>
MODULE_LICENSE ("GPL");//开源协议GPL 或者Dual BSD
MODULE_AUTHOR ("TOM");//作者
MODULE_DESCRIPTION ("MY_TEST");//描述此驱动
//EXPORT_NO_SYMBOLS;//不导出函数 可以不写
//EXPORT_SYMBOL(hello_data);//导出hello_data
int test_init(void)
{
printk(KERN_INFO "hello world\n");
return 0;
}
void test_exit(void)
{
printk(KERN_INFO "goodbye world\n");
}
module_init(test_init); //注册DriverEntry
module_exit(test_exit); //注册DriverUnload
内核是如何知道存在这个驱动?又是在何时调用了驱动初始化函数?看来秘密应该就在这个module_init上。我们就从module_init入手,在linux kernel的源码中找寻这个问题的答案。下面是对相关代码的摘录,省略了不相关的部分。(文件位于include/linux/init.h)
/*
* Used for initialization calls..
*/
typedef int (*initcall_t)(void);
/* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*
* The `id' arg to __define_initcall() is needed so that multiple initcalls
* can point at the same handler without causing duplicate-symbol build errors.
*/
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn
#define device_initcall(fn) __define_initcall(fn, 6)
#define __initcall(fn) device_initcall(fn)
/**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
*
* module_init() will either be called during do_initcalls() (if
* builtin) or at module insertion time (if a module). There can only
* be one per module.
*/
#define module_init(x) __initcall(x);
从代码我们可以看到module_init是一个嵌套宏定义,嵌套层次为:
module_init->__initcall->device_initcall->__define_initcall
经过这么多层嵌套,模板中的module_init(test_init)到底最后变成了什么模样?我们做个实验,首先对模板程序进行适当的调整,并命名为driver.c。
// driver.c
typedef int (*initcall_t)(void);
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn
#define device_initcall(fn) __define_initcall(fn, 6)
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);
int test_init(void)
{
return 0;
}
module_init(test_init);
接下在我们使用gcc对driver.c进行预处理
$ gcc -E driver.c
# 1 "driver.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "driver.c"
typedef int (*initcall_t)(void);
# 12 "driver.c"
int test_init(void)
{
return 0;
}
static initcall_t __initcall_test_init6 __used __attribute__((__s