FreeBSD的Loader和内核初始化( 四 )


 static struct sysinit uniquifier ## _sys_init = { subsystem,
 order, func, ident }; DATA_SET(sysinit_set,uniquifier ##
 _sys_init);
#define SYSINIT(uniquifier, subsystem, order, func, ident) 
C_SYSINIT(uniquifier, subsystem, order,
(sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)ident)
宏DATA_SET()展开成MAKE_SET(),宏MAKE_SET()指向所有隐含的
sysinit幻数:/usr/include/linker_set.h
#define MAKE_SET(set, sym) 
static void const * const __set_##set##_sym_##sym = &sym; 
__asm(".section .set." #set ","aw"");
__asm(".long " #sym); 
__asm(".previous")
#endif
#define TEXT_SET(set, sym) MAKE_SET(set, sym)
#define DATA_SET(set, sym) MAKE_SET(set, sym)
回到我们的例子中,经过宏的展开过程,将会产生如下声明:
static struct sysinit announce_sys_init = {
SI_SUB_COPYRIGHT,
SI_ORDER_FIRST,
(sysinit_cfunc_t)(sysinit_nfunc_t) print_CADdr_t,
(void *) copyright
};
static void const *const __set_sysinit_set_sym_announce_sys_init =
&announce_sys_init;
__asm(".section .set.sysinit_set" ","aw"");
__asm(".long " "announce_sys_init");
__asm(".previous");
第一个__asm指令在内核可执行文件中建立一个ELF节(section) 。这发生在内核链接的时候 。
这一节将被命令为.set.sysinit_set 。这一节的内容是一个32位值——announce_sys_init结构的地址,这个结构正是第二个__asm指令所定义的 。第三个__asm指令标记节的结束 。
如果前面有名字相同的节定义语句,节的内容(那个32位值)将被填加到已存在的节里,这样就构造出了一个32位指针数组 。用objdump察看一个内核二进制文件,也许你会注意到里面有这么几个小的节:% objdump -h /kernel
 7 .set.cons_set 00000014 c03164c0 c03164c0 002154c0 2**2
 CONTENTS, ALLOC, LOAD, DATA
 8 .set.kbddriver_set 00000010 c03164d4 c03164d4 002154d4 2**2
 CONTENTS, ALLOC, LOAD, DATA
 9 .set.scrndr_set 00000024 c03164e4 c03164e4 002154e4 2**2
 CONTENTS, ALLOC, LOAD, DATA
10 .set.scterm_set 0000000c c0316508 c0316508 00215508 2**2
 CONTENTS, ALLOC, LOAD, DATA
11 .set.sysctl_set 0000097c c0316514 c0316514 00215514 2**2
 CONTENTS, ALLOC, LOAD, DATA
12 .set.sysinit_set 00000664 c0316e90 c0316e90 00215e90 2**2
 CONTENTS, ALLOC, LOAD, DATA
这一屏信息显示表明节.set.sysinit_set有0x664字节的大小,所以0x664/sizeof(void *)个sysinit对象被编译进了内核 。其它节,如.set.sysctl_set表示其它链接器集合 。通过定义一个类型为struct linker_set的变量,节.set.sysinit_set将被“收集到那个变量里:sys/kern/init_main.c:
 extern struct linker_set sysinit_set; /* XXX */
struct linker_set定义如下:/usr/include/linker_set.h:
 struct linker_set {
int ls_length;
void*ls_items[1];/* ls_length个项的数组, 以NULL结尾 */
};
译者注: 实际上是说,用C语言结构体linker_set来表达那个ELF节 。第一项是sysinit对象的数量,第二项是一个以NULL结尾的数组,数组中是指向那些对象的指针 。回到对mi_startup()的讨论,我们清楚了sysinit对象是如何被组织起来的 。函数mi_startup()将它们排序,并调用每一个对象 。最后一个对象是系统调度器:/usr/include/sys/kernel.h:
enum sysinit_sub_id {
SI_SUB_DUMMY= 0x0000000,/* 不被执行,仅供链接器使用 */
SI_SUB_DONE = 0x0000001,/* 已被处理*/
SI_SUB_CONSOLE = 0x0800000,/* 控制台*/
SI_SUB_COPYRIGHT= 0x0800001,/* 最早使用控制台的对象 */
...
SI_SUB_RUN_SCHEDULER= 0xfffffff /* 调度器:不返回 */
};
系统调度器sysinit对象定义在文件sys/vm/vm_glue.c中,这个对象的入口点是scheduler() 。这个函数实际上是个无限循环,它表示那个进程标识(PID)为0的进程——swapper进程 。前面提到的proc0结构正是用来描述这个进程 。

推荐阅读