FreeBSD 內核中的SYSINIT分析【轉】

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被鏈接時Makefilesetdef0.o被安排最前面這樣ld就把這個初始化函數的  
計數器安排在這個section的最前面. FreeBSD kernel就能從這個section的開頭讀到這個計  
數器也就知道了有多少個初始化函數Makefile中被安排在中間的的是FreeBSD的其餘  
.o
文件最後由setdef1.o壓陣. setdef1.c定義了一個空指針,用以表示這個section的結束  
,
這種安排我把它叫作夾三明治.  

初始化過程的調用被安排在內核 /sys/kern/init_main.cmi_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編輯器

相關文章
相關標籤/搜索