FreeBSD为了应付这种情况,;使用一种叫做SYSINIT的机制.;我们知道FreeBSD使用一种叫做;ELF的二进制目标执行文件格式.;这种文件格式允许文件内部组织成结构化的方式,;文件内部可以由不同的组成部分(section),;FreeBSD正是利用了这种机制.;
FreeBSD使用GNU;GCC作为其C语言编译器,;这种编译器允许在C源程序中嵌入汇编语言代码,;FreeBSD通过在C源程序中加入汇编指令来在目标文件中增加额外的section,;在文件;/sys/sys/linker_set.h中定义如下:;
#ifdef;__alpha__
#define;MAKE_SET(set,;sym);;;;;;
static;void;const;*;const;__set_##set##_sym_##sym;=;&sym
__asm(".align;3");;;;;;;
__asm(".section;.set.";#set;","aw"");;
__asm(".quad;";#sym);;;;
__asm(".previous")
#else
#define;MAKE_SET(set,;sym);;;
#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)
程序一旦在某处调用DATA_SET宏指令,;就会将相应的汇编符号加入到目标文件.;例如:;int;myint;DATA_SET(myset,;myint);
这两句话将导致在目标文件中创建一个myset;section,;并且myint的地址将被放入这个;section中.;
系统的初始化必须按严格的顺序进行,;为此FreeBSD定义了很多子系统的顺序号,;这些顺序连同SYSINIT的许多相关定义在/sys/sys/kernel.h头文件中:;
enum;sysinit_sub_id;{
SI_SUB_DUMMY;;;;=;0x0000000,;;;;/*;not;executed;;for;linker*/
SI_SUB_DONE;;;;;=;0x0000001,;;;;/*;processed*/
SI_SUB_CONSOLE;;=;0x0800000,;;;;/*;console*/
SI_SUB_COPYRIGHT=;0x0800001,;;;;/*;first;use;of;console*/
SI_SUB_TUNABLES;=;0x0700000,;;;;/*;establish;tunable;values;*/
SI_SUB_VM;;;;;;;=;0x1000000,;;;;/*;virtual;memory;system;init*/
SI_SUB_KMEM;;;;;=;0x1800000,;;;;/*;kernel;memory*/
SI_SUB_KVM_RSRC;=;0x1A00000,;;;;/*;kvm;operational;limits*/
SI_SUB_CPU;;;;;;=;0x1e00000,;;;;/*;CPU;resource(s)*/
SI_SUB_KLD;;;;;;=;0x1f00000,;;;;/*;KLD;and;module;setup;*/
SI_SUB_INTRINSIC=;0x2000000,;;;;/*;proc;0*/
SI_SUB_VM_CONF;;=;0x2100000,;;;;/*;config;VM,;set;limits*/
SI_SUB_RUN_QUEUE=;0x2200000,;;;;/*;the;run;queue*/
SI_SUB_CREATE_INIT;;;;;;=;0x2300000,;;;;/*;create;the;init;process;*/
SI_SUB_DRIVERS;;=;0x2400000,;;;;/*;Let;Drivers;initialize;*/
SI_SUB_CONFIGURE=;0x3800000,;;;;/*;Configure;devices;*/
SI_SUB_VFS;;;;;;=;0x4000000,;;;;/*;virtual;file;system*/
SI_SUB_CLOCKS;;;=;0x4800000,;;;;/*;real;time;and;stat;clocks*/
SI_SUB_MBUF;;;;;=;0x5000000,;;;;/*;mbufs*/
SI_SUB_CLIST;;;;=;0x5800000,;;;;/*;clists*/
SI_SUB_SYSV_SHM;=;0x6400000,;;;;/*;System;V;shared;memory*/
SI_SUB_SYSV_SEM;=;0x6800000,;;;;/*;System;V;semaphores*/
SI_SUB_SYSV_MSG;=;0x6C00000,;;;;/*;System;V;message;queues*/
SI_SUB_P1003_1B;=;0x6E00000,;;;;/*;P1003.1B;realtime;*/
SI_SUB_PSEUDO;;;=;0x7000000,;;;;/*;pseudo;devices*/
SI_SUB_EXEC;;;;;=;0x7400000,;;;;/*;execve();handlers;*/
SI_SUB_PROTO_BEGIN;;;;;;=;0x8000000,;;;;/*;XXX:;set;splimp;(kludge)*/
...
};
子系统内还有顺序号:
enum;sysinit_elem_order;{
SI_ORDER_FIRST;;=;0x0000000,;;;;/*;first*/
SI_ORDER_SECOND;=;0x0000001,;;;;/*;second*/
SI_ORDER_THIRD;;=;0x0000002,;;;;/*;third*/
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的结束;,这种安排,;我把它叫做夹三明治.;
初始化过程的调用被安排在内核;/sys/kern/init_main.c的mi_startup函数中,;mi_startup;是系统启动过程中,;第一个被执行的C语言函数,;它做的第一件事情就是调用这些初始化函数,;开始时对所有的初始化过程做优先级排序,;然后顺序调用它们.;
void;;;;
mi_startup(void)
{;;;;;;;
register;struct;sysinit;**sipp;;/*;system;initialization*/
register;struct;sysinit;**xipp;;/*;interior;loop;of;sort*/
register;struct;sysinit;*save;;;/*;bubble*/
restart:
这是优先级别排序,;这里没有使用那个在setdef0.c中定义的计数器,;而是使用
了setdef1.c中定义的空指针作为结束标志.
/*;;;;;;
*;Perform;a;bubble;sort;of;the;system;initialization;objects;by
*;their;subsystem;(primary;key);and;order;(secondary;key).
*/;;;;;
for;(sipp;=;sysinit;;*sipp;;sipp++);{
for;(xipp;=;sipp;+;1;;*xipp;;xipp++);{
if;((*sipp)->subsystem;< (*xipp)->subsystem;||
((*sipp)->subsystem;==;(*xipp)->subsystem;&&
(*sipp)->order;<= (*xipp)->order))
continue/*;skip*/
save;=;*sipp;
*sipp;=;*xipp;
*xipp;=;save;
}
}
/*
*;Traverse;the;(now);ordered;list;of;system;initialization;tasks.
*;Perform;each;task,;and;continue;on;to;the;next;task.
*
*;The;last;item;on;the;list;is;expected;to;be;the;scheduler,
*;which;will;not;return.
*/
for;(sipp;=;sysinit;;*sipp;;sipp++);{
if;((*sipp)->subsystem;==;SI_SUB_DUMMY)
continue/*;skip;dummy;task(s)*/
这是按顺序调用:
/*
*;Traverse;the;(now);ordered;list;of;system;initialization;tasks.
*;Perform;each;task,;and;continue;on;to;the;next;task.
*
*;The;last;item;on;the;list;is;expected;to;be;the;scheduler,
*;which;will;not;return.
*/
for;(sipp;=;sysinit;;*sipp;;sipp++);{
if;((*sipp)->subsystem;==;SI_SUB_DUMMY)
continue/*;skip;dummy;task(s)*/
if;((*sipp)->subsystem;==;SI_SUB_DONE)
continue;
/*;Call;function;*/
(*((*sipp)->func))((*sipp)->udata);
/*;Check;off;the;one;we're;just;done;*/
(*sipp)->subsystem;=;SI_SUB_DONE;
/*;Check;if;we've;installed;more;sysinit;items;via;KLD;*/
if;(newsysinit;!=;NULL);{
if;(sysinit;!=;(struct;sysinit;**)sysinit_set.ls_items)
free(sysinit,;M_TEMP);
sysinit;=;newsysinit;
newsysinit;=;NULL;
goto;restart;
}
}
panic("Shouldn't;get;here!");
}
标签: