本文编辑整理自: http://www.linuxidc.com/Linux/2011-09/43354.htm
先看这些宏的定义(定义在文件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中)
#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中。
__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_initcall 的含义,就知道了它其实是分别将这些初始化标号修饰的函数指针放到各自的section中的。
SECTION“ .initcall ” level ”. init ”被放入 INITCALLS ( include/asm-generic/vmlinux.lds.h )
#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);
关于SECTION的更多内容请参考《
Linux下的lds链接脚本简介》