經(jīng)歷了跟體系結(jié)構(gòu)密切相關(guān)的匯編代碼之后,就可以進(jìn)入C語(yǔ)言編寫的結(jié)構(gòu)無關(guān)的代碼了。
這個(gè)入口的函數(shù)是start_kernel函數(shù),它主要更進(jìn)一步地初始化系統(tǒng)相關(guān)的內(nèi)容,以便系統(tǒng)進(jìn)入一種服務(wù)狀態(tài),提供一種虛擬機(jī)的服務(wù),提供各種API調(diào)用的服務(wù)。
在start_kernel函數(shù)里,需要非常注意的是里面初始化函數(shù)的順序,這些初始化函數(shù)不能隨便調(diào)換初始化順序,否則就會(huì)導(dǎo)致系統(tǒng)運(yùn)行出錯(cuò)。
由于這個(gè)函數(shù)的內(nèi)容非常多,涉及的內(nèi)容也非常廣泛,每個(gè)函數(shù)都有一個(gè)比較大的概念,一種原理,一種想法。
因此,對(duì)于這個(gè)函數(shù)的學(xué)習(xí)需要很多時(shí)間,需要有漫長(zhǎng)學(xué)習(xí)的心理準(zhǔn)備。
由于本書基于ARM體系的一種結(jié)構(gòu)學(xué)習(xí),其它與此體系結(jié)構(gòu)無關(guān)的代碼,就不再分析介紹。好了,現(xiàn)在就來開始學(xué)習(xí)第一節(jié)的內(nèi)容,代碼如下:
asmlinkage void __init start_kernel(void){ char * command_line; extern const struct kernel_param __start___param[], __stop___param[];
看了這段代碼,首先發(fā)現(xiàn)asmlinkage和__init與一般開發(fā)C語(yǔ)言的應(yīng)用程序有著明顯的差別,導(dǎo)致看不懂這兩個(gè)宏到底是用來做干什么用的。
其實(shí)這兩個(gè)宏是寫內(nèi)核代碼的一種特定表示,一種盡可能快的思想表達(dá),一種盡可能占用空間少的思路。
asmlinkage是一個(gè)宏定義,它的作用主要有兩個(gè),一個(gè)是讓傳送給函數(shù)的參數(shù)全部使用棧式傳送,不用寄存器來傳送。
因?yàn)榧拇嫫鞯膫€(gè)數(shù)有限,使用棧可以傳送更多的參數(shù),比如在X86的CPU里只能使用6個(gè)寄存器傳送,只能傳送4個(gè)參數(shù),而使用棧就沒有這種限制;另外一個(gè)用處是聲明這個(gè)函數(shù)是給匯編代碼調(diào)用的。
不過在ARM體系里,并沒有使用棧傳送參數(shù)的特性,原因何在?由于ARM體系的寄存器個(gè)數(shù)比較多,多達(dá)13個(gè),這樣絕大多數(shù)的函數(shù)參數(shù)都可以通過寄存器來傳送,達(dá)到高效的目標(biāo)。
因此,看到文件./include/linux/linkage.h里的asmlinkage宏定義如下:
#include #include #ifdef __cplusplus#define CPP_ASMLINKAGE extern "C"#else#define CPP_ASMLINKAGE#endif#ifndef asmlinkage#define asmlinkage CPP_ASMLINKAGE#endif#ifndef asmregparm# define asmregparm#endif
在這里可以看到asmlinkage,其實(shí)沒有定義,所以ARM體系里還是通過寄存器來傳送參數(shù)的。如果看一下X86下的代碼,就會(huì)定義如下:
#ifdef CONFIG_X86_32#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
這里定義asmlinkage為通過棧傳送參數(shù)。
接著來看另外一個(gè)宏定義__init,這個(gè)宏定義主要用來標(biāo)志這個(gè)函數(shù)編譯出來的目標(biāo)代碼放在那一段里。
對(duì)于應(yīng)用程序的編譯和連接,不需要作這樣的考慮,但是對(duì)于內(nèi)核代碼來說,就需要了,因?yàn)椴煌亩未a有著不同的作用,比如初始化段的代碼,當(dāng)系統(tǒng)運(yùn)行正常以后,這段代碼就沒有什么用了,聰明的做法就是回收這段代碼占用的內(nèi)存,讓內(nèi)核的代碼占最少的內(nèi)存。
還有另外一個(gè)作用,比如同一段的代碼都是編譯在一起,讓相關(guān)聯(lián)的代碼盡可能同在一片內(nèi)存里,這樣當(dāng)CPU加載代碼到緩存時(shí),就可以一起命中,提高緩存的命中率,這樣就大大提高代碼的執(zhí)行速度。
宏__init定義在文件./include/linux/init.h里,代碼如下:
/* These are for everybody (although not all archs will actually discard it in modules) */#define __init __section(.init.text) __cold notrace
使用這個(gè)宏聲明的函數(shù),編譯時(shí)就會(huì)把目標(biāo)代碼放到段.init.text里,這段都是放置初始化的代碼。
最后看到聲明一個(gè)字符的指針command_line,這個(gè)指針是指向命令行參數(shù)的指針,主要用來指向引導(dǎo)程序傳送給內(nèi)核的命令行參數(shù),在后面的函數(shù)setup_arch和函數(shù)setup_command_line就會(huì)對(duì)它進(jìn)行處理。
smp_setup_processor_id();
緊跟參數(shù)后面的,就是調(diào)用函數(shù)smp_setup_processor_id()了,這個(gè)函數(shù)主要作用是獲取當(dāng)前正在執(zhí)行初始化的處理器ID。
如果仔細(xì)地閱讀完初始化函數(shù)start_kernel,就會(huì)發(fā)現(xiàn)里面還有調(diào)用smp_processor_id()函數(shù),這兩個(gè)函數(shù)都是獲取多處理器的ID,為什么會(huì)需要兩個(gè)函數(shù)呢?
其實(shí)這里有一個(gè)差別的,smp_setup_processor_id()函數(shù)可以不調(diào)用setup_arch()初始化函數(shù)就可以使用,而smp_processor_id()函數(shù)是一定要調(diào)用setup_arch()初始化函數(shù)后,才能使用。
smp_setup_processor_id()函數(shù)是直接獲取對(duì)稱多處理器的ID,而smp_processor_id()函數(shù)是獲取變量保存的處理器ID,因此一定要調(diào)用初始化函數(shù)。
由于smp_setup_processor_id()函數(shù)不用調(diào)用初始化函數(shù),可以放在內(nèi)核初始化start_kernel函數(shù)的最前面使用,而函數(shù)smp_processor_id()只能放到setup_arch()函數(shù)調(diào)用的后面使用了。
smp_setup_processor_id()函數(shù)每次都要中斷CPU去獲取ID,這樣效率比較低。在這個(gè)函數(shù)里,還需要懂得另外一個(gè)概念,就是對(duì)稱多處理器(SymmetricalMulti-Processing)。
由于單處理器的頻率已經(jīng)慢慢變得不能再高了,那么處理器的計(jì)算速度還要提高,還有別的辦法嗎?這樣自然就想到多個(gè)處理器的技術(shù)。
這就好比物流公司,有很多貨只讓一輛卡車在高速公路上來回運(yùn)貨,這樣車的速度已經(jīng)最快了,運(yùn)的貨就一定了,不可能再多得去。
那么物流公司想提高運(yùn)貨量,那只能多顧用幾臺(tái)卡車了,這樣運(yùn)貨量就比以前提高了。處理器的制造廠家自然也想到這樣的辦法,就是幾個(gè)處理器放到一起,這樣就可以提高處理速度。
接著下來的問題又來,那么這幾個(gè)處理器怎么樣放在一起,成本最低,性能最高??紤]到這樣的一種情況,處理器只有共享主內(nèi)存、總線、外圍設(shè)備,其它每個(gè)處理器是獨(dú)立的,這樣可以省掉很多外圍硬件成本。
當(dāng)然所有這些處理器還共享一個(gè)操作系統(tǒng),這樣的結(jié)構(gòu)就叫做對(duì)稱多處理器(SymmetricalMulti-Processing)。在對(duì)稱多處理器系統(tǒng)里,所有處理器只有在初始化階段處理有主從之分,到系統(tǒng)初始化完成之后,大家是平等的關(guān)系,沒有主從處理器之分了。
在內(nèi)核里所有以smp開頭的函數(shù)都是處理對(duì)稱多處理器相關(guān)內(nèi)容的,對(duì)稱多處理器與單處理器在操作系統(tǒng)里,主要區(qū)別是引導(dǎo)處理器與應(yīng)用處理器,每個(gè)處理器不同的緩存,中斷協(xié)作,鎖的同步。因此,在內(nèi)核初始化階段需要區(qū)分,在與緩存同步數(shù)據(jù)需要處理,在中斷方面需要多個(gè)處理協(xié)作執(zhí)行,在多個(gè)進(jìn)程之間要做同步和通訊。如果內(nèi)核只是有單處理器系統(tǒng),smp_setup_processor_id()函數(shù)是是空的,不必要做任保的處理。
/* * Need to run as early as possible, to initialize the * lockdep hash: */ lockdep_init();
這個(gè)函數(shù)主要作用是初始化鎖的狀態(tài)跟蹤模塊。由于內(nèi)核大量使用鎖來進(jìn)行多進(jìn)程、多處理器的同步操作,那么死鎖就會(huì)在代碼不合理時(shí)出現(xiàn),這時(shí)要知道那個(gè)鎖造成的,真是比較困難的。
遇到這種情況,就需要想辦法知道那個(gè)鎖造成的,因此就需要跟蹤鎖的使用狀態(tài),以便發(fā)現(xiàn)出錯(cuò)時(shí),把鎖的狀態(tài)打印出來。
造成死鎖的情況有很多,主要有以下幾種:
1. 同一個(gè)進(jìn)程遞歸地加鎖同一把鎖。
2. 同一把鎖在兩次中斷里加鎖。
3. 幾把鎖形成一個(gè)閉環(huán)死鎖。
debug_objects_early_init();
這個(gè)函數(shù)主要作用是對(duì)調(diào)試對(duì)象進(jìn)行早期的初始化,其實(shí)就是HASH鎖和靜態(tài)對(duì)象池進(jìn)行初始化。
/* * Set up the the initial canary ASAP: */ boot_init_stack_canary(); cgroup_init_early();
cgroup_init_early()這個(gè)函數(shù)主要作用是控制組進(jìn)行早期的初始化。
什么叫控制組(controlgroups)呢?簡(jiǎn)單地說,控制組就是定義一組進(jìn)程具有相同資源的占有程度。
比如可以指定一組進(jìn)程使用CPU為30%,磁盤IO為40%,網(wǎng)絡(luò)帶寬為50%。因此通過控制組就可以把所有進(jìn)程分配不同的資源。
可以參考這個(gè)文檔(http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Resource_Management_Guide/ch01.html)。
local_irq_disable();
這個(gè)函數(shù)主要作用是關(guān)閉當(dāng)前CPU的所有中斷響應(yīng)。在ARM體系里主要就是對(duì)CPSR寄存器進(jìn)行操作。
early_boot_irqs_off();
這個(gè)函數(shù)主要作用是標(biāo)記內(nèi)核還在早期初始化代碼階段,并且中斷在關(guān)閉狀態(tài),如果有任何中斷打開或請(qǐng)求中斷的事情出現(xiàn),都是會(huì)提出警告,以便跟蹤代碼錯(cuò)誤情況。
早期代碼初始化結(jié)束之后,就會(huì)調(diào)用函數(shù)early_boot_irqs_on來設(shè)置這個(gè)標(biāo)志為真。
/* * Interrupts are still disabled. Do necessary setups, then * enable them */ tick_init();
這個(gè)函數(shù)主要作用是初始化時(shí)鐘事件管理器的回調(diào)函數(shù),比如當(dāng)時(shí)鐘設(shè)備添加時(shí)處理。
在內(nèi)核里定義了時(shí)鐘事件管理器,主要用來管理所有需要周期性地執(zhí)行任務(wù)的設(shè)備。
boot_cpu_init();
這個(gè)函數(shù)主要作用是設(shè)置當(dāng)前引導(dǎo)系統(tǒng)的CPU在物理上存在,在邏輯上可以使用,并且初始化準(zhǔn)備好。
在多CPU的系統(tǒng)里,內(nèi)核需要管理多個(gè)CPU,那么就需要知道系統(tǒng)有多少個(gè)CPU,在內(nèi)核里使用cpu_present_map位圖表達(dá)有多少個(gè)CPU,每一位表示一個(gè)CPU的存在。
如果是單個(gè)CPU,就是第0位設(shè)置為1。
雖然系統(tǒng)里有多個(gè)CPU存在,但是每個(gè)CPU不一定可以使用,或者沒有初始化,在內(nèi)核使用cpu_online_map位圖來表示那些CPU可以運(yùn)行內(nèi)核代碼和接受中斷處理。
隨著移動(dòng)系統(tǒng)的節(jié)能需求,需要對(duì)CPU進(jìn)行節(jié)能處理,比如有多個(gè)CPU運(yùn)行時(shí)可以提高性能,但花費(fèi)太多電能,導(dǎo)致電池不耐用,需要減少運(yùn)行的CPU個(gè)數(shù),或者只需要一個(gè)CPU運(yùn)行。
這樣內(nèi)核又引入了一個(gè)cpu_possible_map位圖,表示最多可以使用多少個(gè)CPU。
在本函數(shù)里就是依次設(shè)置這三個(gè)位圖的標(biāo)志,讓引導(dǎo)的CPU物理上存在,已經(jīng)初始化好,最少需要運(yùn)行的CPU。
page_address_init();
這個(gè)函數(shù)主要作用是初始化高端內(nèi)存的映射表。
在這里引入了高端內(nèi)存的概念,那么什么叫做高端內(nèi)存呢?為什么要使用高端內(nèi)存呢?其實(shí)高端內(nèi)存是相對(duì)于低端內(nèi)存而存在的,那么先要理解一下低端內(nèi)存了。
在32位的系統(tǒng)里,最多能訪問的總內(nèi)存是4G,其中3G空間給應(yīng)用程序,而內(nèi)核只占用1G的空間。
因此,內(nèi)核能映射的內(nèi)存空間,只有1G大小,但實(shí)際上比這個(gè)還要小一些,大概是896M,另外128M空間是用來映射高端內(nèi)存使用的。
因此0到896M的內(nèi)存空間,就叫做低端內(nèi)存,而高于896M的內(nèi)存,就叫高端內(nèi)存了。
如果系統(tǒng)是64位系統(tǒng),當(dāng)然就沒未必要有高端內(nèi)存存在了,因?yàn)?4位有足夠多的地址空間給內(nèi)核使用,訪問的內(nèi)存可以達(dá)到10G都沒有問題。
在32位系統(tǒng)里,內(nèi)核為了訪問超過1G的物理內(nèi)存空間,需要使用高端內(nèi)存映射表。
比如當(dāng)內(nèi)核需要讀取1G的緩存數(shù)據(jù)時(shí),就需要分配高端內(nèi)存來使用,這樣才可以管理起來。
使用高端內(nèi)存之后,32位的系統(tǒng)也可以訪問達(dá)到64G內(nèi)存。
在移動(dòng)操作系統(tǒng)里,目前還沒有這個(gè)必要,最多才1G多內(nèi)存。
printk(KERN_NOTICE "%s", linux_banner);
這行代碼主要作用是在輸出終端上顯示版本信息、編譯的電腦用戶名稱、編譯器版本、編譯時(shí)間。如下所示:
Linuxversion 2.6.37+ (tony@tony-desktop) (gcc version 4.3.3 (Sourcery G++Lite 2009q1-203) ) #40 Tue Mar 20 17:49:58 CST 2012
linux_banner是在文件kernel/init/version.c里定義,這個(gè)字符串是由編譯腳本自動(dòng)生成。
setup_arch(&command_line);
這個(gè)函數(shù)主要作用是對(duì)內(nèi)核架構(gòu)進(jìn)行初始化。
再次獲取CPU類型和系統(tǒng)架構(gòu),分析引導(dǎo)程序傳入的命令行參數(shù),進(jìn)行頁(yè)面內(nèi)存初始化,處理器初始化,中斷早期初始化等等。
mm_init_owner(&init_mm, &init_task);
這個(gè)函數(shù)主要作用是設(shè)置最開始的初始化任務(wù)屬于init_mm內(nèi)存。在ARM里,這個(gè)函數(shù)為空。
setup_command_line(command_line);
這個(gè)函數(shù)主要作用是保存命令行,以便后面可以使用。
setup_nr_cpu_ids();
這個(gè)函數(shù)主要作用是設(shè)置最多有多少個(gè)nr_cpu_ids結(jié)構(gòu)。
setup_per_cpu_areas();
這個(gè)函數(shù)主要作用是設(shè)置SMP體系每個(gè)CPU使用的內(nèi)存空間,同時(shí)拷貝初始化段里數(shù)據(jù)。
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
這個(gè)函數(shù)主要作用是為SMP系統(tǒng)里引導(dǎo)CPU進(jìn)行準(zhǔn)備工作。在ARM系統(tǒng)單核里是空函數(shù)。
build_all_zonelists(NULL);
這個(gè)函數(shù)主要作用是初始化所有內(nèi)存管理節(jié)點(diǎn)列表,以便后面進(jìn)行內(nèi)存管理初始化。
page_alloc_init();
這個(gè)函數(shù)主要作用是設(shè)置內(nèi)存頁(yè)分配通知器。
printk(KERN_NOTICE "Kernel command line: %s ", boot_command_line);
這行代碼主要作用是輸出命令參數(shù)到顯示終端。
parse_early_param();
這個(gè)函數(shù)主要作用是分析命令行最早使用的參數(shù)。
parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, &unknown_bootoption);
這行代碼主要對(duì)傳入內(nèi)核參數(shù)進(jìn)行解釋,如果不能識(shí)別的命令就調(diào)用最后參數(shù)的函數(shù)。
/* * These use large bootmem allocations and must precede * kmem_cache_init() */ pidhash_init();
這個(gè)函數(shù)是進(jìn)程ID的HASH表初始化,這樣可以提供通PID進(jìn)行高效訪問進(jìn)程結(jié)構(gòu)的信息。
LINUX里共有四種類型的PID,因此就有四種HASH表相對(duì)應(yīng)。
vfs_caches_init_early();
這個(gè)函數(shù)是虛擬文件系統(tǒng)的緩存初始化。
sort_main_extable();
這個(gè)函數(shù)是對(duì)內(nèi)核內(nèi)部的異常表進(jìn)行堆排序,以便加速訪問。
trap_init();
這個(gè)函數(shù)是對(duì)異常進(jìn)行初始化,在ARM系統(tǒng)里是空函數(shù),沒有任何的初始化。
mm_init();
這個(gè)函數(shù)是標(biāo)記那些內(nèi)存可以使用,并且告訴系統(tǒng)有多少內(nèi)存可以使用,當(dāng)然是除了內(nèi)核使用的內(nèi)存以外。
/* * Set up the scheduler prior starting any interrupts (such as the * timer interrupt). Full topology setup happens at smp_init() * time - but meanwhile we still have a functioning scheduler. */ sched_init();
這個(gè)函數(shù)主要作用是對(duì)進(jìn)程調(diào)度器進(jìn)行初始化,比如分配調(diào)度器占用的內(nèi)存,初始化任務(wù)隊(duì)列,設(shè)置當(dāng)前任務(wù)的空線程,當(dāng)前任務(wù)的調(diào)度策略為CFS調(diào)度器。
/* * Disable preemption - early bootup scheduling is extremely * fragile until we cpu_idle() for the first time. */ preempt_disable();
這個(gè)函數(shù)主要作用是關(guān)閉優(yōu)先級(jí)調(diào)度。由于每個(gè)進(jìn)程任務(wù)都有優(yōu)先級(jí),目前系統(tǒng)還沒有完全初始化,還不能打開優(yōu)先級(jí)調(diào)度。
if (!irqs_disabled()) { printk(KERN_WARNING "start_kernel(): bug: interrupts were " "enabled *very* early, fixing it "); local_irq_disable(); }
這段代碼主要判斷是否過早打開中斷,如果是這樣,就會(huì)提示,并把中斷關(guān)閉。
rcu_init();
這個(gè)函數(shù)是初始化直接讀拷貝更新的鎖機(jī)制。
RCU主要提供在讀取數(shù)據(jù)機(jī)會(huì)比較多,但更新比較的少的場(chǎng)合,這樣減少讀取數(shù)據(jù)鎖的性能低下的問題。
radix_tree_init();
這個(gè)函數(shù)是初始化radix樹,radix樹基于二進(jìn)制鍵值的查找樹。
/* init some links before init_ISA_irqs() */ early_irq_init();
init_IRQ();
這個(gè)函數(shù)是初始化中斷相關(guān)的工作,主要初始化中斷描述數(shù)組,然后調(diào)用每個(gè)CPU架構(gòu)中斷初始化。
prio_tree_init();
這個(gè)函數(shù)是初始化優(yōu)先搜索樹,主要用在內(nèi)存反向搜索方面。
init_timers();
這個(gè)函數(shù)是主要初始化引導(dǎo)CPU的時(shí)鐘相關(guān)的數(shù)據(jù)結(jié)構(gòu),注冊(cè)時(shí)鐘的回調(diào)函數(shù),當(dāng)時(shí)鐘到達(dá)時(shí)可以回調(diào)時(shí)鐘處理函數(shù),最后初始化時(shí)鐘軟件中斷處理。
hrtimers_init();
這個(gè)函數(shù)是初始化高精度的定時(shí)器,并設(shè)置回調(diào)函數(shù)。
softirq_init();
這個(gè)函數(shù)是初始化軟件中斷,軟件中斷與硬件中斷區(qū)別就是中斷發(fā)生時(shí),軟件中斷是使用線程來監(jiān)視中斷信號(hào),而硬件中斷是使用CPU硬件來監(jiān)視中斷。
timekeeping_init();
這個(gè)函數(shù)是初始化系統(tǒng)時(shí)鐘計(jì)時(shí),并且初始化內(nèi)核里與時(shí)鐘計(jì)時(shí)相關(guān)的變量。
time_init();
這個(gè)函數(shù)是初始化系統(tǒng)時(shí)鐘。
profile_init();
這個(gè)函數(shù)是分配內(nèi)核性能統(tǒng)計(jì)保存的內(nèi)存,以便統(tǒng)計(jì)的性能變量可以保存到這里。
if (!irqs_disabled()) printk(KERN_CRIT "start_kernel(): bug: interrupts were " "enabled early ");
這兩行代碼是提示中斷是否過早地打開。
early_boot_irqs_on();
這個(gè)函數(shù)是設(shè)置內(nèi)核還在早期初始化階段的標(biāo)志,以便用來調(diào)試時(shí)輸出信息。
local_irq_enable();
這個(gè)函數(shù)是打開本CPU的中斷,也即允許本CPU處理中斷事件,在這里打開引CPU的中斷處理。如果有多核心,別的CPU還沒有打開中斷處理。
/* Interrupts are enabled now so all GFP allocations are safe. */ gfp_allowed_mask = __GFP_BITS_MASK; kmem_cache_init_late();
/* * HACK ALERT! This is early. We're enabling the console before * we've done PCI setups etc, and console_init() must be aware of * this. But we do want output early, in case something goes wrong. */ console_init();
這個(gè)函數(shù)是用來初始化控制臺(tái),從這個(gè)函數(shù)之后就可以輸出內(nèi)容到控制臺(tái)了。
在這個(gè)函數(shù)初化之前,都沒有辦法輸出內(nèi)容,就是輸出,也是寫到輸出緩沖區(qū)里,緩存起來,等到這個(gè)函數(shù)調(diào)用之后,就立即輸出內(nèi)容。
if (panic_later) panic(panic_later, panic_param);
這段代碼是判斷分析輸入的參數(shù)是否出錯(cuò),如果有出錯(cuò),就啟動(dòng)控制臺(tái)輸出之后,立即打印出錯(cuò)的參數(shù),以便用戶立即看到出錯(cuò)的地方。
lockdep_info();
這個(gè)函數(shù)是打印鎖的依賴信息,用來調(diào)試鎖。通過這個(gè)函數(shù)可以查看目前鎖的狀態(tài),以便可以發(fā)現(xiàn)那些鎖產(chǎn)生死鎖,那些鎖使用有問題。
/* * Need to run this when irqs are enabled, because it wants * to self-test [hard/soft]-irqs on/off lock inversion bugs * too: */ locking_selftest();
這個(gè)函數(shù)是用來測(cè)試鎖的API是否使用正常,進(jìn)行自我測(cè)試。比如測(cè)試自旋鎖、讀寫鎖、一般信號(hào)量和讀寫信號(hào)量。
#ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) { printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " "disabling it. ", page_to_pfn(virt_to_page((void *)initrd_start)), min_low_pfn); initrd_start = 0; }#endif page_cgroup_init();
page_cgroup_init()這個(gè)函數(shù)是容器組的頁(yè)面內(nèi)存分配。
enable_debug_pagealloc();
這個(gè)函數(shù)是設(shè)置內(nèi)存分配是否需要輸出調(diào)試信息,如果調(diào)用這個(gè)函數(shù),當(dāng)分配內(nèi)存時(shí),不會(huì)輸出一些相關(guān)的信息。
kmemleak_init();
debug_objects_mem_init();
這個(gè)函數(shù)是創(chuàng)建調(diào)試對(duì)象內(nèi)存緩存,所以緊跟內(nèi)存緩存初始化后面
idr_init_cache();
這個(gè)函數(shù)是創(chuàng)建IDR機(jī)制的內(nèi)存緩存對(duì)象。所謂的IDR就是整數(shù)標(biāo)識(shí)管理機(jī)制(integerIDmanagement)。
引入的主要原因是管理整數(shù)的ID與對(duì)象的指針的關(guān)系,由于這個(gè)ID可以達(dá)到32位,也就是說,如果使用線性數(shù)組來管理,那么分配的內(nèi)存太大了;如果使用線性表來管理,又效率太低了,所以就引用IDR管理機(jī)制來實(shí)現(xiàn)這個(gè)需求。
setup_per_cpu_pageset();
這個(gè)函數(shù)是創(chuàng)建每個(gè)CPU的高速緩存集合數(shù)組。因?yàn)槊總€(gè)CPU都不定時(shí)需要使用一些頁(yè)面內(nèi)存和釋放頁(yè)面內(nèi)存,為了提高效率,就預(yù)先創(chuàng)建一些內(nèi)存頁(yè)面作為每個(gè)CPU的頁(yè)面集合。
numa_policy_init();
這個(gè)函數(shù)是初始化NUMA的內(nèi)存訪問策略。所謂NUMA,它是NonUniform Memory AccessAchitecture的縮寫,主要用來提高多個(gè)CPU訪問內(nèi)存的速度。因?yàn)槎鄠€(gè)CPU訪問同一個(gè)節(jié)點(diǎn)的內(nèi)存速度遠(yuǎn)遠(yuǎn)比訪問多個(gè)節(jié)點(diǎn)的速度來得快。
if (late_time_init) late_time_init();
這段代碼是主要運(yùn)行時(shí)鐘相關(guān)后期的初始化功能。
sched_clock_init();
calibrate_delay();
這個(gè)函數(shù)是主要計(jì)算CPU需要校準(zhǔn)的時(shí)間,這里說的時(shí)間是CPU執(zhí)行時(shí)間。如果是引導(dǎo)CPU,這個(gè)函數(shù)計(jì)算出來的校準(zhǔn)時(shí)間是不需要使用的,主要使用在非引導(dǎo)CPU上,因?yàn)榉且龑?dǎo)CPU執(zhí)行的頻率不一樣,導(dǎo)致時(shí)間計(jì)算不準(zhǔn)確。
pidmap_init();
這個(gè)函數(shù)是進(jìn)程位圖初始化,一般情況下使用一頁(yè)來表示所有進(jìn)程占用情況。
anon_vma_init();
這個(gè)函數(shù)是初始化反向映射的匿名內(nèi)存,提供反向查找內(nèi)存的結(jié)構(gòu)指針位置,快速地回收內(nèi)存。
#ifdef CONFIG_X86 if (efi_enabled) efi_enter_virtual_mode();#endif
這段代碼是初始化EFI的接口,并進(jìn)入虛擬模式。EFI是ExtensibleFirmware Interface的縮寫,就是INTEL公司新開發(fā)的BIOS接口。
thread_info_cache_init();
這個(gè)函數(shù)是線程信息的緩存初始化。
cred_init();
fork_init(totalram_pages);
這個(gè)函數(shù)是根據(jù)當(dāng)前物理內(nèi)存計(jì)算出來可以創(chuàng)建進(jìn)程(線程)的數(shù)量,并進(jìn)行進(jìn)程環(huán)境初始化。
proc_caches_init();
這個(gè)函數(shù)是進(jìn)程緩存初始化。
buffer_init();
這個(gè)函數(shù)是初始化文件系統(tǒng)的緩沖區(qū),并計(jì)算最大可以使用的文件緩存。
key_init();
這個(gè)函數(shù)是初始化安全鍵管理列表和結(jié)構(gòu)。
security_init();
這個(gè)函數(shù)是初始化安全管理框架,以便提供訪問文件/登錄等權(quán)限。
dbg_late_init();
vfs_caches_init(totalram_pages);
這個(gè)函數(shù)是虛擬文件系統(tǒng)進(jìn)行緩存初始化,提高虛擬文件系統(tǒng)的訪問速度。
signals_init();
這個(gè)函數(shù)是初始化信號(hào)隊(duì)列緩存。
/* rootfs populating might need page-writeback */ page_writeback_init();
#ifdef CONFIG_PROC_FS proc_root_init();#endif
這個(gè)函數(shù)是初始化系統(tǒng)進(jìn)程文件系統(tǒng),主要提供內(nèi)核與用戶進(jìn)行交互的平臺(tái),方便用戶實(shí)時(shí)查看進(jìn)程的信息。
cgroup_init();
這個(gè)函數(shù)是初始化進(jìn)程控制組,主要用來為進(jìn)程和其子程提供性能控制。比如限定這組進(jìn)程的CPU使用率為20%。
cpuset_init();
這個(gè)函數(shù)是初始化CPUSET,CPUSET主要為控制組提供CPU和內(nèi)存節(jié)點(diǎn)的管理的結(jié)構(gòu)。
taskstats_init_early();
這個(gè)函數(shù)是初始化任務(wù)狀態(tài)相關(guān)的緩存、隊(duì)列和信號(hào)量。任務(wù)狀態(tài)主要向用戶提供任務(wù)的狀態(tài)信息。
delayacct_init();
這個(gè)函數(shù)是初始化每個(gè)任務(wù)延時(shí)計(jì)數(shù)。當(dāng)一個(gè)任務(wù)等CPU運(yùn)行,或者等IO同步時(shí),都需要計(jì)算等待時(shí)間。
check_bugs();
這個(gè)函數(shù)是用來檢查CPU配置、FPU等是否非法使用不具備的功能。
acpi_early_init(); /* before LAPIC and SMP init */
這個(gè)函數(shù)是初始化ACPI電源管理。
高級(jí)配置與能源接口(ACPI)ACPI規(guī)范介紹ACPI能使軟、硬件、操作系統(tǒng)(OS),主機(jī)板和外圍設(shè)備,依照一定的方式管理用電情況,系統(tǒng)硬件產(chǎn)生的Hot-Plug事件,讓操作系統(tǒng)從用戶的角度上直接支配即插即用設(shè)備,不同于以往直接通過基于BIOS 的方式的管理。
sfi_init_late();
ftrace_init();
這個(gè)函數(shù)是初始化內(nèi)核跟蹤模塊,ftrace的作用是幫助開發(fā)人員了解Linux?內(nèi)核的運(yùn)行時(shí)行為,以便進(jìn)行故障調(diào)試或性能分析。
/* Do the rest non-__init'ed, we're now alive */ rest_init();}
這個(gè)函數(shù)是后繼初始化,主要是創(chuàng)建內(nèi)核線程init,并運(yùn)行。
?
評(píng)論
查看更多