FreeBSD 内核中的SYSINIT分析( 二 )


SI_ORDER_MIDDLE;=;0x1000000,;;;;/*;somewhere;in;the;middle;*/
SI_ORDER_ANY;;;;=;0xfffffff;;;;;/*;last*/
};



FreeBSD为每个想要在系统初始化时被调用的函数,;定义两个函数类型:
typedef;void;(*sysinit_nfunc_t);__P((void;*));
typedef;void;(*sysinit_cfunc_t);__P((const;void;*));
它们是系统初始化被调用时使用的函数原型.
两个重要的宏使得初始化函数能够在系统开始时被执行:
#define;C_SYSINIT(uniquifIEr,;subsystem,;order,;func,;ident);;;;
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)


其中每个初始化函数被存储成这样一个结构:
struct;sysinit;{
unsigned;int;;;;subsystem;;;;;;;/*;subsystem;identifier*/
unsigned;int;;;;order;;;/*;init;order;within;subsystem*/
sysinit_cfunc_t;func;;;;/*;function;;;;;*/
const;void;;;;;;*udata;;/*;multiplexer/argument;*/
};

这个结构包含了子系统编号,;子系统中的顺序号,;初始化函数的地址,;以及这个函数使用的参数.;

现在如果有个函数想要在系统启动时自动被调用,;并且知道这个函数是为VM子系统做准备工作,;可以这样申明:;

long;myvar;
void;init_myvar(void;*p)
{
*(long;*)p;=;2;
}
SYSINIT(init_myvar,;SI_SUB_VM,;1000,;init_myvar,;&myvar)

这样声明的初始化过程分布在很多目标文件中,;当gcc的连接编辑器ld运行时就会把属于同一个section的数据合并到一个连续的地址块中.;由于在这个section中包含的只能是指向sysinit结构的指针,这样FreeBSD就可以把这个地址当成一个sysinit*;的数组,;FreeBSD找出这个sysinit_set地址,;边历这个数组并调用其中的初始化函数.;为了确切知道这个section的大小(直接读ELF是可能的,但是那样太复杂,要知道kernel调用初始化过程时文件系统可能还没有初始化呢),;系统中包含一个工具;gensetdefs,;这个工具能扫描给出的一组.o目标文件,;并找到任何名字是由.set.开头的;section,;它统计有多少个这样的的初始化函数,;并在sysinit_set的开头生成一个长整形计数器.;gensetdefs生成三个文件:
setdef0.c;setdef1.c;setdefs.h

文件setdef0.c的内容:;


--------------------------------------------------------
/*;THIS;FILE;IS;GENERATED,;DO;NOT;EDIT.;*/

#define;DEFINE_SET(set,;count);;
__asm__(".section;.set.";#set;","aw"")
__asm__(".globl;";#set);
__asm__(".type;";#set;",@object");;;;;;;
__asm__(".p2align;2");;;
__asm__(#set;":");;;;;;;
__asm__(".long;";#count)
__asm__(".previous")

#include;"setdefs.h";;;;/*;Contains;a;`DEFINE_SET";for;each;set;*/
--------------------------------------------------------

这里的DEFINE_SET效果就是申明一C结构:
struct;linker_set;{
int;;;;;ls_length;
void;;;;*ls_items[1];;;;/*;really;ls_length;of;them,
*;trailing;NULL;*/
};

文件setdef1.c的内容:

--------------------------------------------------------
/*;THIS;FILE;IS;GENERATED,;DO;NOT;EDIT.;*/

#define;DEFINE_SET(set,;count);;
__asm__(".section;.set.";#set;","aw"")
__asm__(".long;0");;;;;;
__asm__(".previous")

#include;"setdefs.h";;;;/*;Contains;a;`DEFINE_SET";for;each;set;*/

这个DEFINE_SET在某个section中放入一个;long;0.
--------------------------------------------------------

文件setdefs.h的内容:

DEFINE_SET(cons_set,;3);
DEFINE_SET(kbddriver_set,;2);
DEFINE_SET(periphdriver_set,;5);
DEFINE_SET(scrndr_set,;9);
DEFINE_SET(scterm_set,;1);
DEFINE_SET(sysctl_set,;552);
DEFINE_SET(sysinit_set,;323);
DEFINE_SET(sysuninit_set,;155);
DEFINE_SET(vga_set,;9);
DEFINE_SET(videodriver_set,;4);

当kernel被连接时,;在Makefile中setdef0.o被安排最前面,;这样ld就把这个初始化函数的计数器安排在这个section的最前面.;FreeBSD;kernel就能从这个section的开头读到这个计数器,;也就知道了有多少个初始化函数.;在Makefile中被安排在中间的的是FreeBSD的其他;.o文件,;最后由setdef1.o压阵.;setdef1.c定义了一个空指针,用以表示这个section的结束;,这种安排,;我把它叫做夹三明治.;

推荐阅读