先看这些宏的定义(定义在文件include/linux/init.h中)
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define
device_initcall(fn)
__define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
这些宏都用到了__define_initcall(),再看看它的定义(一样定义在文件include/linux/init.h中)
这其中
initcall_t
是函数指针,原型以下,
typedef int (*initcall_t)(void);
而编译器宏
__attribute__((__section__()))
则表示把对象放在一个这个由括号中的名称所指代的section中。
__define_initcall("6",fn,6) 则是把fn的地址放到.
initcall
6
.
init
这个selection中
另外,注意这里
static
的做用,只是当前编译单元可见,从全局来看的
__initcall_##fn##id
完整名字,是要加当前编译文件名(或称当前编译单位名)做为前缀的
因此__define_initcall的含义是:
1)
声明一个名称为__initcall_##fn的函数指针;
2)
将这个函数指针初始化为fn;
3)
编译的时候须要把这个函数指针变量放置到名称为
".
initcall
"
level
"
.init
"的section中
#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)
VMLINUX_SYMBOL(
__early_initcall_end)
=
.
;
这里的
.
是一个特殊的符号,它是定位器,一个位置指针,指向程序地址空间内的某位置(或某section内的偏移,若是它在SECTIONS命令内的某section描述内),该符号只能在SECTIONS命令内使用。
这句命令的意思,是对
__early_initcall_end
变量赋值为
当前section内的偏移
*(
.initcall7s.init
)
表示的是
全部输入文件
中的名为
.initcall7s.init
的
SECTIONS
__initcall_start
和
__initcall_end
以及
INITCALLS
中定义的SECTION都是在arch/xxx/kernel/vmlinux.lds.S中放在
.init
段的。
SECTIONS
{
.
init
: {
__initcall_start
=
.
;
INITCALLS
__initcall_end
=
.
;
}
}
.
init
只是用于对
SECTIONS
进行分类
__initcall_start
=
.
;
和
__initcall_end
=
.
;
表示分别对变量
__initcall_start
和
__initcall_end
赋值为
当前section内的偏移。
而这些
SECTION
里的函数在初始化时被顺序执行(init内核线程->
do_basic_setup()
[main.c#778]->
do_initcalls()
)。
程序(
init/main.c
文件
do_initcalls()
函数)以下,
do_initcalls()
把
XX.initcallXX.init
中的函数按顺序都执行一遍。
for
(call =
__early_initcall_end
; call <
_
_initcall_end
; call++)
do_one_initcall(*call);