参考了网上的很多内容,网上的分析基本上是基于linux2.6或者3.1的内核,对于这个函数而言,其实大同小异,但是几乎没有哪篇文章能一次性把我想要了解的东西全部呈现,所以自己尝试整理如下:
do_initcalls()->
static void __init do_initcalls(void)
{
initcall_t *fn;
for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
}
/linux-3.3/arm/kernel/vmlinux.lds中有下述定义:
__initcall_start = .;
*(.initcallearly.init) __early_initcall_end
= .;
*(.initcall0.init)
*(.initcall0s.init)
*(.initcall1.init)
*(.initcall1s.init)
*(.initcall2.init)
*(.initcall2s.init)
*(.initcall3.init)
*(.initcall3s.init)
*(.initcall4.init)
*(.initcall4s.init)
*(.initcall5.init)
*(.initcall5s.init)
*(.initcallrootfs.init)
*(.initcall6.init)
*(.initcall6s.init)
*(.initcall7.init)
*(.initcall7s.init) __initcall_end = .;
与之类似,
在include\asm-generic中有下述定义:
#define INITCALLS \
*(.initcallearly.init) \
VMLINUX_SYMBOL(__early_initcall_end)
= .; \
*(.initcall0.init) \
*(.initcall0s.init) \
*(.initcall1.init) \
*(.initcall1s.init) \
*(.initcall2.init) \
*(.initcall2s.init) \
*(.initcall3.init) \
*(.initcall3s.init) \
*(.initcall4.init) \
*(.initcall4s.init) \
*(.initcall5.init) \
*(.initcall5s.init) \
*(.initcallrootfs.init) \
*(.initcall6.init) \
*(.initcall6s.init) \
*(.initcall7.init) \
*(.initcall7s.init)
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start)= .; \
INITCALLS \
VMLINUX_SYMBOL(__initcall_end)= .;
由上面分析可以看出do_initcalls的作用就是遍历一个指针数组,数组内容是对应level的函数指针, 所以这里要干的事情就是依次取出各个level的函数指针fn执行(*fn)回调,
也就是实现了依次遍历各个level的驱动程序。
接着来看do_one_initcall(*fn)这个函数做了什么事情;
int __init_or module do_one_initcall(initcall_t fn)
{
int count=preempt_count();
int ret;
if(initcall_debug)
ret=do_one_initcall_debug(fn);
else
ret=fn();
.......
省略的部分是发生错误后的处理函数。
我想要分析的主要是具体设备的驱动初始化过程,所以关注的是module_init()的过程,这对应上面指针数组的哪一段呢,可以从下面的分析中得到:
#define __initcall(fn) device_initcall(fn);
#define module_init(x) __initcall(x);
#define module_exit(x) __exitcall(x);
所以module_init(fn)与__initcall(fn)以及device_initcall(fn)这三者是等价的。
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall"level ".init"))) = fn
->
#define __used __attribute__((__unused__))
#define __used attribute((unused))
在gcc手册中找到了有关的解释:
unused:This attribute, attached to a function, means that the function is meant to be possibly unused. GCC will not produce a warning for this function.
表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息
->
__attribute__((__section__(".initcall"level ".init")))=fn;
__attribute__((section("section_name")))
其作用是将作用的函数或数据放入指定名为"section_name"对应的段中,这句话的意思就是把fn放在“.initcall”
level”.init”段中,
且定义__define_initcall(level,fn,id)与__initcall_##fn##id的意义一致。
具体驱动在内核中加载顺序可以从文件Omapl138/linux3.3/System.map中得到:
在这里用nandflash的驱动举例:
module_init(nand_davinci_init);
module_exit(nand_davinci_exit);
展开上面的宏:
module_init(nand_davinci_init)
->
__initcall(nand_davinci_init)
->
device_initcall(nand_davinci_init)
->
__define_initcall("6", nand_davinci_init,6)
->
static initcall_t __initcall_nand_davinvi_init6__used\
__attribue_((__section_(.”initcall6.init”)))=nand_davinci_init;
明天再具体分析其他驱动的加载流程