Minix3原始碼分析(2)——系統初始化
minix3的啟動牽扯到幾次控制權轉移,它們發生在mpx386.s中的組合語言例程和start.c及main.c中的C語言例程之間。
彙編程式碼需要做許多工作,包括建立一個 棧幀 以便為C編譯器編譯的程式碼提供適當的環境,複製處理器所使用的表格來定義儲存器段,建立各種CPU暫存器,等等。這些工作結束後,初始化過程通過呼叫 cstart(start.c) 繼續進行。
cstart() 呼叫另一個例程來初始化 全域性描述符表(GDT) ,這是Intel 32位處理器 實現保護模式的核心資料結構; 以及 中斷描述符表(LDT) ,它用來 為每種可能的中斷型別選擇執行程式碼。 在從 cstart() 返回之後,lgdt
lgdt (_gdt + GDT_SELECTOR)
lidt (_gdt + IDT_SELECTOR)
jmpf CS_SELECTOR:csinit
csinit:
o16 mov ax, DS_SELECTOR
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
o16 mov ax, TSS_SELECTOR
ltr ax
push 0
popf
jmp _main ! 跳轉回main()處
cstart()
start.c
/*
* 這個檔案中包含了Intel處理器體系下的minix的啟動程式碼。它和mpx386.s一起為main()
* 建立一個良好的環境。
* 對於16位系統,這段程式碼執行在真實模式(real mode)下,並且必須切換到保護模式下對
* 於286機器。
* 對於32位系統,它已經執行在了保護模式(protected mode)下,但選擇符仍然由禁用
* 中斷的BIOS提供,因此需要重新載入描述符並建立中斷描述符。
*/
FORWARD _PROTOTYPE( char *get_value, (_CONST char *params, _CONST char *key));
/*
* 在呼叫main()之前執行進行初始化。
* 大多數設定是在minix載入程式傳遞的環境字串的幫助下確定的。
*/
PUBLIC void cstart(cs, ds, mds, parmoff, parmsize)
U16_t cs, ds; /* 核心程式碼段和資料段 */
U16_t mds; /* 監控資料段 */
U16_t parmoff, parmsize; /* 啟動引數的偏移量和長度 */
{
char params[128 * sizeof(char *)]; /* 啟動時的監控引數 */
register char *value; /* key=value鍵值對中的value */
extern int etext, end;
/* 首先確定是否是保護模式;386機器或者更高的版本意味著保護模式。
* 這個工作必須首先完成,因為這是seg2phys()需要的。
* 對於286機器,我們還不能決定保護模式,這是下面要做的。
*/
#if _WORD_SIZE != 2 /* 286機器 */
machine.protected = 1;
#endif
/* 記錄核心和監控器的位置 */
kinfo.code_base = seg2phys(cs);
kinfo.code_size = (phys_bytes) &etext; /* 程式碼段的大小 */
kinfo.data_base = seg2phys(ds);
kinfo.data_size = (phys_bytes) &end; /* 資料段的大小 */
/* 初始化保護模式的描述符,建立CPU的保護機制和中斷表 */
prot_init();
/* 將啟動引數拷貝到本地緩衝區(記憶體的核心部分) */
kinfo.params_base = seg2phys(mds) + parmoff;
kinfo.params_size = MIN(parmsize, sizeof(params) - 2);
phys_copy(kinfo.params_base, vir2phys(params), kinfo.params_size);
/* 記錄使用者空間伺服器的雜項資訊 */
kinfo.nr_procs = NR_PROCS;
kinfo.nr_tasks = NR_TASKS;
strncpy(kinfo.release, OS_RELEASE, sizeof(kinfo.release));
kinfo.release[sizeof(kinfo.release) - 1] = '\0';
strncpy(kinfo.version, OS_VERSION, sizeof(kinfo.version));
kinfo.version[sizeof(kinfo.version) - 1] = '\0';
kinfo.proc_addr = (vir_bytes) proc;
kinfo.kmem_base = vir2phys(0);
kinfo.kmem_size = (phys_bytes) &end;
/* 確定那些老的機器是否處於保護模式 */
machine.processor = atoi(get_value(params, "processor"));
#if _WORD_SIZE == 2
machine.protected = machine.processor >= 286;
#endif
if (! machine.protected) mon_return = 0;
/* XT,AT or MCA匯流排 */
value = get_value(params, "bus");
if (value == NIL_PTR || strcmp(value, "at") == 0)
machine.pc_at = TRUE; /* PC-AT相容的硬體 */
else if (strcmp(value, "mca") == 0)
machine.pc_at = machine.ps_mca = TRUE;
/* VDU型別 */
value = get_value(params, "video"); /* EGA or VGA視訊單元 */
if (strcmp(value, "ega") == 0) machine.vdu_ega = TRUE;
if (strcmp(value, "vga") == 0) machine.vdu_vga = machine.vdu_ega = TRUE;
/* 返回到彙編程式碼,如果是286機器,則切換到保護模式,
* 重新載入選擇符並呼叫main()。
*/
}
PRIVATE char *get_value(params, name)
_CONST char *params; /* 啟動時的監控引數 */
_CONST char *name; /* 待查詢的鍵值 */
{
/* 獲取環境值-getenv()函式的核心版本,以避免設定通常的環境陣列 */
register _CONST char *namep;
register char *envp;
for (envp = (char *) params; *envp != 0;) {
for (namep = name; *namep != 0 && *namep == *envp; namep++, envp++)
;
if (*namep == '\0' && *envp == '=')
return (envp + 1);
while (*envp++ != 0)
;
}
return (NIL_PTR);
}
這裡需要說明一下上述程式碼中出現的幾個巨集。
#define PRIVATE static
#define FORWARD static /* 一些編譯器要求它是靜態的,用在有些函式的宣告中 */
#define PUBLIC
這幾個巨集只是為了程式碼的可讀性。
ansi.h
#ifdef _ANSI /* ANSI C編譯器 */
#define _PROTOTYPE(function, params) function params
#define _ARGS(params) params
#define _VOIDSTAR void *
#define _VOID void
#define _CONST const
#define _VOLATILE volatile
#define _SIZET size_t
#else /* K&R C編譯器 */
#define _PROTOTYPE(function, params) function()
#define _ARGS(params) ()
#define _VOIDSTAR void *
#define _VOID void
#define _CONST
#define _VOLATILE
#define _SIZET int
#endif /* _ANSI */
_PROTOTYPE 巨集貫穿整個系統,所有的函式宣告都以這種方式給出。它的出現是為了相容ANSI C編譯器和老式的K&R C編譯器。其他的一些型別巨集以後也都會遇見的。
之後由 main() 完成初始化,然後開始系統的正常執行。main() 程式碼的主要部分用來建立 程序表和特權表。這樣當排程到第一批任務和程序時,它們的記憶體映象和暫存器及特權資訊能夠被正確地設定。
34~38行的迴圈用來初始化pproc_addr陣列,這個陣列用來加快程序表的訪問。40~44行的迴圈清除了特權表以及與程序表及其項陣列類似的ppriv_addr陣列。
對於程序表和特權表而言,在它們的一個域中放入一個特定值可以將其標記為未被使用。但對於每一個表項,無論是否被使用,都需要使用一個索引號來初始化。
56~126行的長迴圈 使用執行引導映像所需的必要資訊來初始化程序表。所有這些程序在啟動時必須存在,而且在正常執行過程中不應該中止。
在迴圈開始處,ip被設定為table.c中建立的映象表項的地址。rp是一個指向程序表項的指標,而 priv(rp) 則指向特權表的表項。在這個長迴圈中,程序表和特權表的初始化很多都是由兩個過程組成的:先從映象表中讀取一個值,然後再將其存入程序表或特權表。
71行對核心部分的程序進行測試,如果為真,則將 任務棧的基地址 儲存為特定的 STACK_GUARD 模式。這樣可以稍後檢查以確保棧沒有溢位。然後為每一個任務建立 初始的棧指標。而且每個任務都需要自己私有的棧指標。這是因為在記憶體中,棧是向下增長的,所以初始棧指標可以通過當前基地址和任務棧的大小相加來得到(76行)。但是有一個例外:KERNEL程序(有些地方也標識為HARDWARE)一直都是 非就緒態,永遠不會作為一個普通程序來執行,因此也就不需要棧指標。
下面我們先看一下a.out.h中的strcut exec結構。該結構定義了可執行檔案的格式。
struct exec { /* a.out header */
unsigned char a_magic[2]; /* 魔數 */
unsigned char a_flags; /* 標誌位 */
unsigned char a_cpu; /* cpu id */
unsigned char a_hdrlen; /* header的長度 */
unsigned char a_unused; /* 留作將來使用 */
unsigned short a_version; /* 版本標記(目前還未使用) */
long a_text; /* 文字段的大小 */
long a_data; /* 資料段的大小 */
long a_bss; /* bss段的大小 */
long a_entry; /* 入口點 */
long a_total; /* 分配的總記憶體 */
long a_syms; /* 符號表的大小 */
long a_trsize; /* 文字段重定位的大小 */
long a_drsize; /* 資料段重定位的大小 */
long a_tbase; /* 文字段重定位的基地址 */
long a_dbase; /* 資料段重定位的基地址 */
};
在88行,其中一個頭檔案複製到當地的struct exec結構e_hdr,並使用hdrindex作為標頭檔案陣列的索引。然後文字段和資料段的地址被轉換為clicks並進入該程序的記憶體對映區域。
80行的hdrindex總是被賦為0。核心中的程序全部被編譯進了核心檔案,而有關棧請求的資訊則都在映象表中。由於任務被編譯進了核心並且可以呼叫核心中的任意程式碼和訪問核心中任意位置的資料,所以一個獨立任務的大小是沒有什麼意義的。
核心和每一個任務都讀取aout陣列中的同一元素,而一個任務的表徵其大小的域則按核心大小進行賦值。任務從映象表中獲取它們的棧資訊,在編譯table.c時進行初始化。
處理完所有的核心程序後,該迴圈每執行一次,hdrindex就加1(82行),所以所有的使用者空間程序都可以從它們的標頭檔案中得到正確的資料。
106和107行設定了程式計數器的初始值和處理器狀態字。任務的處理器狀態字與裝置驅動程式和伺服器是不同的,因為任務擁有較高的優先順序,可以訪問I/O埠。而如果是一個使用者空間程序,它的棧指標則被初始化。
程序表中有一個入口不需要(也不能夠)被排程。程序HARDWARE(任務)只是為了記賬操作的目的,它記錄中斷服務所用的時間。所有其他程序在118和119行中被放置到適當的佇列中。函式lock_enqueue在修改佇列之前關閉中斷,修改完成後再開啟中斷。
對程序表初始化的最後一步是在125行呼叫alloc_segments。這是一個與機器相關的過程,它將各程序使用的記憶體段的位置、大小及執行特權級設定到適當的域中。對一種記憶體分配方法相異的處理器型別,必須重寫alloc_segments。
一旦所有任務、伺服器和init初始化完成,系統基本上就可以運行了。變數bill_ptr標明對哪個程序進行CPU使用的計費,它需要一個初值,該初值在131行賦值。此時,IDLE程序是一個合適的選擇。這時核心已經可以正常工作了。
雖然並非系統所有的其他部分都已能夠正常執行,但它們都已能作為獨立的程序執行,並被標記為就緒和等待執行。當它們執行時會初始化自身。
核心然後呼叫announce來宣告它已就緒,之後呼叫restart。main函式的任務到此為止,它的工作只是完成初始化。136行對restart的呼叫將啟動第一個任務,控制權從此不再返回到main.
main.c
/*
* 這個檔案包含了minix的主程式和關機部分的程式碼。
* main()初始化系統並通過設定程序表、中斷向量以及排程每個要執行的任務來初始化
* 自身,從而開始系統的執行;shutdown()關閉系統。
*/
FORWARD _PROTOTYPE( void announce, (void));
FORWARD _PROTOTYPE( void shutdown, (timer_t *tp));
PUBLIC int main(void)
{
/* 開始執行 */
struct boot_image *ip; /* 引導映像指標 */
register struct proc *rp; /* 程序指標 */
register struct priv *sp; /* 特權結構指標 */
register int i, s;
int hdrindex;
phys_clicks text_base;
vir_clicks text_clicks, data_clicks;
reg_t ktsb; /* 核心任務棧基地址 */
struct exec e_hdr;
/* 初始化中斷控制器 */
intr_init(1);
/* intr_init()之所以放在這裡,是因為此前必須知道機器型別(因為intr_init呼叫
* 完全依賴於硬體)。
* 引數(1)指示intr_init是在為minix3進行初始化,minix終止並將控制權返回給
* 引導監控程式時可以通過使用引數(0)再次初始化硬體,使其回到原始狀態。
* intr_init保證在完成初始化之前,任何中斷都不會生效。
*/
/* 初始化pproc_addr陣列,這個陣列用於加快程序表的訪問 */
for (rp = BEG_PROC_ADDR, i = -NR_TASKS; rp < END_PROC_ADDR; ++rp, ++i) {
rp->p_rts_flags = SLOT_FREE; /* 標誌為空閒 */
rp->p_nr = i; /* proc number from ptr */
(pproc_addr + NR_TASKS)[i] = rp; /* proc ptr from number */
}
/* 清除特權表和ppriv_addr陣列 */
for (sp = BEG_PRIV_ADDR, i = 0; sp < END_PRIV_ADDR; ++sp, ++i) {
sp->s_proc_nr = NONE; /* 初始化為NONE */
sp->s_id = i; /* priv結構索引 */
ppriv_addr[i] = sp; /* priv ptr from number */
}
/* 為任務和伺服器設定程序表條目。核心任務的堆疊初始化為資料空間中的陣列。
* 監視器已將伺服器堆疊新增到資料段,因此堆疊指標被設定為指向資料段的末尾。
* 8086上的所有程序都處於低記憶體中。而在386上,只有核心處於低記憶體中,其餘記憶體
* 都載入到了擴充套件記憶體中。
*/
/* 任務棧 */
ktsb = (reg_t) t_stack;
/* 使用執行引導映像所需的必要資訊來初始化程序表 */
for (i = 0; i < NR_BOOT_PROCS; ++i) {
ip = &image[i]; /* 程序屬性 */
rp = proc_addr(ip->proc_nr); /* 獲得程序指標 */
rp->p_max_priority = ip->priority; /* 最大排程優先順序 */
rp->p_priority = ip->priority; /* 當前優先順序 */
rp->p_quantum_size = ip->quantum; /* 時鐘滴答數 */
rp->p_ticks_left = ip->quantum; /* current credit */
strncpy(rp->p_name, ip->proc_name, P_NAME_LEN); /* 設定程序名 */
(void)get_priv(rp, (ip->flags & SYS_PROC)); /* 分配結構 */
priv(rp)->s_flags = ip->flags; /* 程序標誌位 */
priv(rp)->s_trap_mask = ip->trap_mask; /* 允許陷阱 */
priv(rp)->s_call_mask = ip->call_mask; /* 核心呼叫掩碼 */
priv(rp)->s_ipc_to.chunk[0] = ip->ipc_to; /* 限制目標 */
if (iskerneln(proc_nr(rp))) { /* 是核心部分 */
if (ip->stksize > 0) { /* HARDWARE棧大小為0? */
rp->p_priv->s_stack_guard = (reg_t *) ktsb;
*rp->p_priv->s_stack_guard = STACK_GUARD;
}
ktsb += ip->stksize; /* 指向棧的高階 */
rp->p_reg.sp = ktsb; /* 該任務的初始化棧指標 */
text_base = kinfo.code_base >> CLICK_SHIFT;
/* 核心中的程序 */
hdrindex = 0; /* 都使用第一個a.out header */
} else
hdrindex = 1 + i - NR_TASKS; /* 伺服器,驅動程式,init程序 */
/* 引導載入程式在絕對地址'aout'處建立了一個a.out標頭陣列。
* 並且將一個元素賦給e_hdr。
* 巨集#define A_MINHDR 32(a.out.h)定義了相關機器型別
*/
phys_copy(aout + hdrindex * A_MINHDR, vir2phys(&e_hdr),
(phys_bytes) A_MINHDR);
/* 將地址轉換為clicks,並構建程序記憶體對映 */
text_base = e_hdr.a_syms >> CLICK_SHIFT;
text_clicks = (e_hdr.a_text + CLICK_SIZE-1) >> CLICK_SHIFT;
if (!(e_hdr.a_flags & A_SEP)) text_clicks = 0; /* common I&D */
data_clicks = (e_hdr.a_total + CLICK_SIZE-1) >> CLICK_SHIFT;
rp->p_memmap[T].mem_phys = text_base;
rp->p_memmap[T].mem_len = text_clicks;
rp->p_memmap[D].mem_phys = text_base + text_clicks;
rp->p_memmap[D].mem_len = data_clicks;
rp->p_memmap[S].mem_phys = text_base + text_clicks + data_clicks;
rp->p_memmap[S].mem_vir = data_clicks; /* empty - stack is in data */
/* 設定初始暫存器值。任務的處理器狀態字與其他程序的處理器狀態字不同,
* 因為任務可以訪問I/O埠;對於許可權較低的程序,這是不被允許的。
*/
rp->p_reg.pc = (reg_t) ip->initial_pc;
rp->p_reg.psw = (iskernelp(rp)) ? INIT_TASK_PSW : INIT_PSW;
/* 初始化伺服器棧指標。記下一個字給crtso.s中的argc。 */
if (isusern(proc_nr(rp))) { /* 使用者空間程序? */
rp->p_reg.sp = (rp->p_memmap[S].mem_vir +
rp->p_memmap[S].mem_len) << CLICK_SHIFT;
rp->p_reg.sp -= sizeof(reg_t);
}
/* Set ready. The HARDWARE task is never ready. */
if (rp->p_nr != HARDWARE) {
rp->p_rts_flags = 0; /* 如果沒有標誌位,則可以執行 */
lock_enqueue(rp); /* 新增到排程佇列 */
} else {
rp->p_rts_flags = NO_MAP; /* 禁止執行 */
}
/* 必須在保護模式下分配程式碼段和資料段 */
alloc_segments(rp);
}
/* 我們絕對不會shutdown */
shutdown_started = 0;
/* minix現已準備就緒,所有啟動映像程序都在就緒佇列中。
* 現在返回到彙編程式碼中以開始運行當前程序。
*/
bill_ptr = proc_addr(IDLE); /* 它必須指向某個地方 */
announce(); /* 列印minix啟動banner */
restart();
}
/* 展示minix啟動banner */
PRIVATE void announce(void)
{
kprintf("MINIX %s.%s."
"Copyright 2006, Vrije Universiteit, Amsterdam, The Netherlands\n",
OS_RELEASE, OS_VERSION);
/* 真實模式還是16/32位保護模式? */
kprintf("Executing in %s mode.\n\n",
machine.protected ? "32-bit protected" : "real");
}
/* 準備關閉minix */
PUBLIC void prepare_shutdown(how)
int how;
{
static timer_t shutdown_timer;
register struct proc *rp;
message m;
/* 在panics上顯示除錯轉儲,確保tty任務仍然存在並且可以處理它們,這是通過非阻塞send完成的。
* 在除錯轉儲完成後,我們依賴tty呼叫sys_abort()。
*/
if (how == RBT_PANIC) {
m.m_type = PANIC_DUMPS;
if (nb_send(TTY_PROC_NR,&m)==OK) /* 如果tty沒有準備好,請不要阻止 */
return; /* 等待來自tty的sys_abort() */
}
/* 向仍處於活動狀態的所有系統程序傳送訊號,通知它們minix核心正在關閉。
* 應由使用者空間伺服器完成正確的關閉處理。在系統發生混亂時,此機制可用作備份,
* 因此係統程序仍可執行其關閉程式碼,例如,同步fs或讓tty切換到第一個控制檯。
*/
kprintf("Sending SIGKSTOP to system processes ...\n");
for (rp = BEG_PROC_ADDR; rp < END_PROC_ADDR; rp++) {
if (!isemptyp(rp) && (priv(rp)->s_flags & SYS_PROC) && !iskernelp(rp))
send_sig(proc_nr(rp), SIGKSTOP);
}
/* 我們正在關機,診斷現在可能會有不同的表現 */
shutdown_started = 1;
/* 通知即將關閉的系統程序,並允許它們通過設定一個shutdown()中的watchog
* 計時器來安排自己。計時器引數傳遞關閉狀態。
*/
kprintf("MINIX will now be shut down ...\n");
tmr_arg(&shutdown_timer)->ta_int = how;
/* 1s後繼續,讓程序有機會安排關機工作 */
set_timer(&shutdown_timer, get_uptime() + HZ, shutdown);
}
/*
* 從prepare_shutdown()或stop_sequence()呼叫此函式來關閉minix。
* 關閉引數(如何關閉):
* RBT_HALT(返回監視器),
* RBT_MONITOR(執行給定程式碼),
* RBT_RESET(硬復位)
*/
PRIVATE void shutdown(tp
相關推薦
Minix3原始碼分析(2)——系統初始化
minix3的啟動牽扯到幾次控制權轉移,它們發生在mpx386.s中的組合語言例程和start.c及main.c中的C語言例程之間。
彙編程式碼需要做許多工作,包括建立一個 棧幀 以便為C編譯器編譯的程式碼提供適當的環境,複製處理器所使用的表格來定義儲存器段,建
05.Fabric原始碼分析–kvledger的初始化
Fabric原始碼分析5–kvledger的初始化
前兩篇文章藉由/fabric/peer/main.go這個線頭,簡單分析了fabric的配置和日誌系統。雖然還有一部分可說的內容,如common.InitCrypto()呼叫,但現在暫且按下main.go不管,而把目光投到/fabric
Netty原始碼分析:1.3初始化NioServerSocketChannel
第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開
Netty原始碼分析:1.1初始化NioEventLoopGroup
第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開
比特幣原始碼分析--P2P網路初始化
區塊鏈和AI無疑是近期業界當之無愧的兩大風口。AI就不說了,區塊鏈從17年各種數字貨幣被炒上了天,一下成為了人們街頭巷議的焦點,本文撇開數字貨幣的投資不說,僅僅從技術層面來剖析一下區塊鏈各個部分的原理。畢竟目前已經有包括BAT等巨頭在內的許多公司投入到了區塊鏈的研發
Bootstrap原始碼分析系列之初始化和依賴項
在上一節中我們介紹了Bootstrap整體架構,本節我們將介紹Bootstrap框架第二部分初始化及依賴項,這部分內容位於原始碼的第8~885行,開啟原始碼這部分內容似乎也不是很難理解。但是請站在一個開發者的角度來面對這段原始碼。為什麼要這樣寫?如果沒有Bootstrap
Android Wi-Fi原始碼分析之wpa_supplicant初始化(四):wpa_supplicant_init_iface函式分析
wpa_config_read分析
路徑為:external\wpa_supplicant_8\wpa_supplicant\config_file.c
struct wpa_config * wpa_config_read(const char *na
Android Wi-Fi原始碼分析之wpa_supplicant初始化(一)
一. wpa_supplicant配置編譯
將對應的平臺的wpa_supplicant包解壓改名為wpa_supplicant_8替換掉external下的wpa_supplicant_8目錄
執行:
source build/envsetup.sh
Spring Security4.0.3原始碼分析之FilterChainProxy初始化
最近在學習安全框架Spring Security,想弄清楚其中實現的具體步驟,於是下定決心,研究一下Spring Security原始碼,這篇部落格的目的是想把學習過程記錄下來。學習過程中主要參考了http://dead-knight.iteye.com/blo
OpenStack之Neutron原始碼分析 Neutron-server初始化
從資料夾的命名也基本可以得出該目錄程式碼的作用,幾個重要的資料夾如下:
agent: 主要是l3 agent及l3 agent ha的相關程式碼;
common: 主要是各底層驅動與linux系統命令的互動層;
db: 是neutron各功能與資料庫互動資
Android Wi-Fi原始碼分析之wpa_supplicant初始化(三):wpa_supplicant_add_iface函式分析
路徑為:external\wpa_supplicant_8\wpa_supplicant\wpa_supplicant.c
/**
* wpa_supplicant_add_iface - Add a new network interface
* @
Spring MVC原始碼分析之DispatcherServlet初始化過程
DispatcherServelt本質是也是Servlet,由Servlet容器進行載入。
1.Servlet介面提供了Servlet的初始化方法:init(ServletConfig config)。
2.GenericServlet實現了方法init(S
spring boot 原始碼解析2-SpringApplication初始化
前言
我們生成一個spring boot 專案時,會自帶一個啟動類. 程式碼如下:
@SpringBootApplication
public class DemoApplication {
public static void main(St
spring 原始碼分析--IOC容器初始化六
上一節將xml文件解析為DOM ,並且建立了一個 BeanDefinitionParserDelegate 型別的物件,在這一節,將使用這個物件來完成對bean的裝載工作。
2.1.1.1 parseBeanDefinitions (root, delegate): 該方法體完成註冊過程。
spring原始碼分析-web容器初始化過程解析1
在之前的“”中分析了spring mvc的初始化過程,接下來將分析其請求處理過程。
在找請求處理的入口時,我們需要先知道Servlet的程式設計規範,對應不同的請求(如POST、GET等)的實現方法在FrameworkServlet中,分別是doP
Spring原始碼分析之容器初始化
AnnotationConfigApplicationContext
核心類,BeanDefinition註冊器,所有的BeanD
4、Spring原始碼分析4之初始化Bean
1、記錄建立bean的ObjectFactory,目的是為了解決迴圈依賴
if (instanceWrapper == null)
Lighttpd1.4.20原始碼分析 筆記 fdevent系統-初始化
C程式在進行真正的編譯之前都要進行預編譯。
我們看看fdevent系統中的一些巨集:
#if defined(HAVE_EPOLL_CTL) && defined(HAVE_SYS_EPOLL_H)
# if defined HAVE_STD
VINS-Mono代碼分析與總結(二) 系統初始化
exp 簡單 都是 最小值 cnblogs 特征向量 vision 還要 per VINS-Mono代碼註釋:https://github.com/gaochq/VINS-Mono/tree/comment
註釋不完整,可以一起交流。
參考文獻
1 VINS-Mono: A
一:Greenplum5.10.2 生產環境安裝配置 (系統初始化、安裝準備)
添加 dead 有關 zlib 直接 dconf tables .rpm grub.conf 服務簡介:
Greenplum Master
Master只存儲系統元數據,業務數據全部分布在Segments上。其作為整個數據庫系統的入口,負責建立與客戶端的連接,SQL的解析並