内核驱动初始化顺序 ---- __define_initcall

内核驱动初始化顺序:

内核第一个C函数 main.c/start_kernel() ---> rest_init() ---> kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND) ---> kernel_init() ---> do_basic_setup() ---> do_initcalls() ---> do_one_initcall()

 

__define_initcall的作用:

宏定义__define_initcall(level,fn,id)对于内核的初始化很重要,他指示编译器在编译的时候,将一系列初始化函数的起始地址值按照一定的顺序放在一个section中。

在内核初始化阶段,do_initcalls() 将按顺序从该section中以函数指针形式取出这些函数的起始地址,来依次完成相应的初始化。由于内核某些部分的初始化需要依赖于其他某些部分的初始化的完成,因此这个顺序排列常常很重要。

#define module_init(x)	__initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn)		__define_initcall("6",fn,6)
 
1. 分析__define_initcall(level, fn, id) 宏定义:

这个宏定义在 kernel/include/linux/init.h中:

#define __define_initcall(level,fn,id) \
	static initcall_t __initcall_##fn##id __used \
	__attribute__((__section__(".initcall" level ".init"))) = fn


 其中initcall_t 是个函数指针类型:

typedef int (*initcall_t)(void);

而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。
所以这个宏定义的含义是:

a) 声明一个名称为__initcall_##fn的函数指针(其中##表示替换连接);

b) 将这个函数指针初始化为fn;

c) 编译的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"的section中(比如level="1",代表这个section的名称是 ".initcall1.init")。

d) level 值越小表示优先及更高。kernel中 level的范围是(0~7);

 

2. 和初始化相关的section-initcall.init段:

 section-initcall.init 分成7个子section:

__initcall_start = .;
.initcall.init : {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}

Makefile中有:LDFLAGS_vmlinux += -T arch/$(ARCH)/kernel/vmlinux.lds.s

3. 内核初始化函数do_initcalls():

do_initcalls() 将从 .initcall.init 中,也就是这7个section中依次取出任何的函数指针,并调用这些函数指针所指向的函数,来完成内核的一些相关的初始化。

 

参考链接:

http://hi.chinaunix.net/?uid-13720850-action-viewspace-itemid-32336

 

 

你可能感兴趣的:(内核驱动初始化顺序 ---- __define_initcall)