FreeBSD kernel是一個膨大的系統, 對於這樣一個大系統, 裏面每每包含了大量的子系統和
模塊,當系統初始化時這些模塊就須要初始化, 按照一般的思路,這些初始化過程必須在某處
被顯式地調用,這樣一來,當你新增某個模塊,你必須再修改那個系統初始化的地方來調用這
個新增模塊的初始化過程, 並且因爲ANSI C語言的限制,調用某個函數最好先聲明,這樣當系
統的初始化過程開始增長時, 那個調用初始化過程的文件開始大量包含那些原本不相關的頭
文件, 偶合度就增長了, 這是一種很差的設計.
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!");
}
數組
SRC=http://www.moon-soft.com/program/bbs/readelite432617.htm編輯器