十天学Linux内核之第七天---电源开和关时都发生了什么
說(shuō)實(shí)話感覺(jué)自己快寫(xiě)不下去了,其一是有些勉強(qiáng)跟不上來(lái),其二是感覺(jué)自己越寫(xiě)越差,剛開(kāi)始可能是新鮮感以及很多讀者的鼓勵(lì),現(xiàn)在就是想快點(diǎn)完成自己制定的任務(wù),不過(guò)總有幾個(gè)讀者給自己鼓勵(lì),很欣慰的事情,不多感慨了,加緊時(shí)間多多去探索吧,今天要去描述的是電源開(kāi)和關(guān)時(shí)都發(fā)生了什么,一起去看看吧~~
bootloader引導(dǎo)裝入程序?qū)?nèi)核映像加載到內(nèi)存并處理控制權(quán)傳送到內(nèi)核后在內(nèi)核引導(dǎo)時(shí)每個(gè)子系統(tǒng)都必須要初始化,我們根據(jù)實(shí)際執(zhí)行的線性順序跟蹤內(nèi)核的初始化過(guò)程,下圖說(shuō)明了從系統(tǒng)加電到斷電這一過(guò)程中所有事情發(fā)生的順序,這個(gè)圖不多加解釋了,看圖就知道其線性執(zhí)行順序,中間過(guò)程也是很簡(jiǎn)單的步驟啦。
我們首先討論的是BIOS和Open和Fireware,它們分別是x86和PPC系統(tǒng)加電后在只讀內(nèi)存的某一地址(一般是Flash ROM)最先運(yùn)行的代碼,這些代碼負(fù)責(zé)激活系統(tǒng)中相應(yīng)的部分,以便處理內(nèi)核的加載,對(duì)于x86而言,這就是系統(tǒng)BIOS的駐留之處,基本的輸入輸出是一塊引導(dǎo)系統(tǒng)并與硬件相關(guān)的系統(tǒng)初始化代碼,這里不多說(shuō)了,對(duì)于PowerPC而言,初始化代碼的類(lèi)型與PowerPC體系結(jié)構(gòu)的出現(xiàn)時(shí)間有關(guān),詳情就參見(jiàn)Open Fireware 的主頁(yè)www.openfireware.org
引導(dǎo)裝入程序Bootloaders大家應(yīng)該早就有所了解了,Boot Loaders是駐留在計(jì)算機(jī)引導(dǎo)設(shè)備的程序,第一個(gè)引導(dǎo)設(shè)備往往是系統(tǒng)中的第一個(gè)硬盤(pán),完成足夠的系統(tǒng)初始化工作后,BIOS或固件調(diào)用引導(dǎo)裝入程序,一旦成功加載進(jìn)來(lái),內(nèi)核就初始化并配置操作系統(tǒng),對(duì)x86系統(tǒng)而言,BIOS允許用戶(hù)為其系統(tǒng)設(shè)置引導(dǎo)設(shè)備的順序,這里提一下GRUB,Grand Unified Bootloader 是基于x86的引導(dǎo)裝入程序,用來(lái)加載Linux。GRUB2在設(shè)計(jì)之初就考慮了移植到PPC系統(tǒng)的問(wèn)題,哎具體的就www.gnu.org/software/grub上有豐富的文檔,并且百度上也有相關(guān)的例程之類(lèi)的,我覺(jué)得這里我就不再闡釋了。
Linux裝入程序即LILO這個(gè)我還是得說(shuō)說(shuō),和GRUB相類(lèi)似但是LILO僅僅使用配置文件,并且沒(méi)有命令行接口。LILO運(yùn)行時(shí)的第一階段步驟如下:
第一階段:
- 開(kāi)始執(zhí)行并且顯示“L.";
- 檢測(cè)磁盤(pán)幾何信息并且顯示“I.";
- 加載第二階段的代碼。
第二階段:
- 開(kāi)始執(zhí)行并且顯示“L.";
- 確定引導(dǎo)數(shù)據(jù)和操作系統(tǒng)的位置,并且顯示”O(jiān).";
確定啟動(dòng)哪個(gè)操作系統(tǒng)并且跳轉(zhuǎn)到該操作系統(tǒng),LILO配置文件中的一段代碼如下(代碼在etc/lilo.conf上可以查看):
1 image = /boot/bzimage-2.6.7-mytestkernel //image指明內(nèi)核所在地 2 label = Kernel 2.6.7, my test kernel //label描述配置的字符串 3 root = /dev/hda6 //root指明根文件系統(tǒng)駐留的分區(qū) 4 read-only //表明根分區(qū)在引導(dǎo)時(shí)候不可被修改GRUB和LILO的主要區(qū)別
- LILO將配置信息存儲(chǔ)在主引導(dǎo)記錄中,若有任何改動(dòng),必須運(yùn)行/sbin/lilo來(lái)更新主引導(dǎo)記錄
- LILO沒(méi)有交互式的命令行接口
- LILO不能讀取不同的文件系統(tǒng)
最后說(shuō)一下Yaboot,yaboot是另一個(gè)引導(dǎo)程序(比如說(shuō),grub和lilo是比較出名的引導(dǎo)程序),用于Macintosh。主頁(yè):http://yaboot.ozlabs.org/?Yaboot引導(dǎo)時(shí)的步驟如下:
- OF調(diào)用Yaboot
- 找到引導(dǎo)設(shè)備和引導(dǎo)路徑并打開(kāi)引導(dǎo)分區(qū)
- 打開(kāi)/etc/yaboot.conf或命令解釋器
- 加載映像或內(nèi)核以及initrd
- 執(zhí)行映像
這里需要在Ubuntu上自己操作,當(dāng)然對(duì)Ubuntu的基本命令操作是首先要了解的,然后才能根據(jù)步驟一步一步執(zhí)行下去。
?
x86和PowerPC體系結(jié)構(gòu)的硬件初始化,由于內(nèi)存管理的初始化與硬件息息相關(guān),要理解其初始化過(guò)程就必須了解硬件的規(guī)格,那么都只能去看看資料才能了解了,這里我多講不了。現(xiàn)在的PowerPC和x86的代碼都集中在init/main.c的start_kernel()中,該例程位于體系結(jié)構(gòu)無(wú)關(guān)的代碼段,它調(diào)用特定體系結(jié)構(gòu)的例程來(lái)完成內(nèi)存初始化。下面我們來(lái)探究一下start_kernel()函數(shù)。
跳轉(zhuǎn)到start_kernel()時(shí)候,執(zhí)行進(jìn)程0,也就是平時(shí)說(shuō)的超級(jí)用戶(hù)進(jìn)程,進(jìn)程0孕育了進(jìn)程1,也就是init進(jìn)程,然后進(jìn)程0就變成CPU的空閑進(jìn)程,調(diào)/sbin/init時(shí),僅有這兩個(gè)進(jìn)程在運(yùn)行:
1 asmlinkage void __init start_kernel(void) 2 { 3 char * command_line; 4 extern struct kernel_param __start___param[], __stop___param[]; 5 //來(lái)設(shè)置smp process id,當(dāng)然目前看到的代碼里面這里是空的 6 smp_setup_processor_id(); 7 //lockdep是linux內(nèi)核的一個(gè)調(diào)試模塊,用來(lái)檢查內(nèi)核互斥機(jī)制尤其是自旋鎖潛在的死鎖問(wèn)題。 8 //自旋鎖由于是查詢(xún)方式等待,不釋放處理器,比一般的互斥機(jī)制更容易死鎖, 9 //故引入lockdep檢查以下幾種情況可能的死鎖(lockdep將有專(zhuān)門(mén)的文章詳細(xì)介紹,在此只是簡(jiǎn)單列舉): 10 // 11 //·同一個(gè)進(jìn)程遞歸地加鎖同一把鎖; 12 // 13 //·一把鎖既在中斷(或中斷下半部)使能的情況下執(zhí)行過(guò)加鎖操作, 14 // 又在中斷(或中斷下半部)里執(zhí)行過(guò)加鎖操作。這樣該鎖有可能在鎖定時(shí)由于中斷發(fā)生又試圖在同一處理器上加鎖; 15 // 16 //·加鎖后導(dǎo)致依賴(lài)圖產(chǎn)生成閉環(huán),這是典型的死鎖現(xiàn)象。 17 lockdep_init(); 18 debug_objects_early_init(); 19 //初始化stack_canary棧3 20 //stack_canary的是帶防止棧溢出攻擊保護(hù)的堆棧。 21 // 當(dāng)user space的程序通過(guò)int 0x80進(jìn)入內(nèi)核空間的時(shí)候,CPU自動(dòng)完成一次堆棧切換, 22 //從user space的stack切換到kernel space的stack。 23 // 在這個(gè)進(jìn)程exit之前所發(fā)生的所有系統(tǒng)調(diào)用所使用的kernel stack都是同一個(gè)。 24 //kernel stack的大小一般為4096/8192, 25 //內(nèi)核堆棧示意圖幫助大家理解: 26 // 27 // 內(nèi)存低址 內(nèi)存高址 28 // | |<-----------------------------esp| 29 // +-----------------------------------4096-------------------------------+ 30 // | 72 | 4 | x < 4016 | 4 | 31 // +------------------+-----------------+---------------------------------+ 32 // |thread_info | | STACK_END_MAGIC | var/call chain |stack_canary | 33 // +------------------+-----------------+---------------------------------+ 34 // | 28 | 44 | | | 35 // V | | 36 // restart_block V 37 // 38 //esp+0x0 +0x40 39 // +---------------------------------------------------------------------------+ 40 // |ebx|ecx|edx|esi|edi|ebp|eax|ds|es|fs|gs|orig_eax|eip|cs|eflags|oldesp|oldss| 41 // +---------------------------------------------------------------------------+ 42 // | kernel完成 | cpu自動(dòng)完成 | 43 boot_init_stack_canary(); 44 // cgroup: 它的全稱(chēng)為control group.即一組進(jìn)程的行為控制. 45 // 比如,我們限制進(jìn)程/bin/sh的CPU使用為20%.我們就可以建一個(gè)cpu占用為20%的cgroup. 46 // 然后將/bin/sh進(jìn)程添加到這個(gè)cgroup中.當(dāng)然,一個(gè)cgroup可以有多個(gè)進(jìn)程. 47 48 cgroup_init_early(); 49 //更新kernel中的所有的立即數(shù)值,但是包括哪些需要再看? 50 core_imv_update(); 51 //關(guān)閉當(dāng)前CUP中斷 52 local_irq_disable(); 53 //修改標(biāo)記early_boot_irqs_enabled; 54 //通過(guò)一個(gè)靜態(tài)全局變量 early_boot_irqs_enabled來(lái)幫助我們調(diào)試代碼, 55 //通過(guò)這個(gè)標(biāo)記可以幫助我們知道是否在”early bootup code”,也可以通過(guò)這個(gè)標(biāo)志警告是有無(wú)效的終端打開(kāi) 56 early_boot_irqs_off(); 57 //每一個(gè)中斷都有一個(gè)IRQ描述符(struct irq_desc)來(lái)進(jìn)行描述。 58 //這個(gè)函數(shù)的主要作用是設(shè)置所有的 IRQ描述符(struct irq_desc)的鎖是統(tǒng)一的鎖, 59 //還是每一個(gè)IRQ描述符(struct irq_desc)都有一個(gè)小鎖。 60 early_init_irq_lock_class(); 61 62 // 大內(nèi)核鎖(BKL--Big Kernel Lock) 63 //大內(nèi)核鎖本質(zhì)上也是自旋鎖,但是它又不同于自旋鎖,自旋鎖是不可以遞歸獲得鎖的,因?yàn)槟菢訒?huì)導(dǎo)致死鎖。 64 //但大內(nèi)核鎖可以遞歸獲得鎖。大內(nèi)核鎖用于保護(hù)整個(gè)內(nèi)核,而自旋鎖用于保護(hù)非常特定的某一共享資源。 65 //進(jìn)程保持大內(nèi)核鎖時(shí)可以發(fā)生調(diào)度,具體實(shí)現(xiàn)是: 66 //在執(zhí)行schedule時(shí),schedule將檢查進(jìn)程是否擁有大內(nèi)核鎖,如果有,它將被釋放,以致于其它的進(jìn)程能夠獲得該鎖, 67 //而當(dāng)輪到該進(jìn)程運(yùn)行時(shí),再讓它重新獲得大內(nèi)核鎖。注意在保持自旋鎖期間是不運(yùn)行發(fā)生調(diào)度的。 68 //需要特別指出,整個(gè)內(nèi)核只有一個(gè)大內(nèi)核鎖,其實(shí)不難理解,內(nèi)核只有一個(gè),而大內(nèi)核鎖是保護(hù)整個(gè)內(nèi)核的,當(dāng)然有且只有一個(gè)就足夠了。 69 //還需要特別指出的是,大內(nèi)核鎖是歷史遺留,內(nèi)核中用的非常少,一般保持該鎖的時(shí)間較長(zhǎng),因此不提倡使用它。 70 //從2.6.11內(nèi)核起,大內(nèi)核鎖可以通過(guò)配置內(nèi)核使其變得可搶占(自旋鎖是不可搶占的),這時(shí)它實(shí)質(zhì)上是一個(gè)互斥鎖,使用信號(hào)量實(shí)現(xiàn)。 71 //大內(nèi)核鎖的API包括: 72 // 73 //void lock_kernel(void); 74 // 75 //該函數(shù)用于得到大內(nèi)核鎖。它可以遞歸調(diào)用而不會(huì)導(dǎo)致死鎖。 76 // 77 //void unlock_kernel(void); 78 // 79 //該函數(shù)用于釋放大內(nèi)核鎖。當(dāng)然必須與lock_kernel配對(duì)使用,調(diào)用了多少次lock_kernel,就需要調(diào)用多少次unlock_kernel。 80 //大內(nèi)核鎖的API使用非常簡(jiǎn)單,按照以下方式使用就可以了: 81 //lock_kernel(); //對(duì)被保護(hù)的共享資源的訪問(wèn) … unlock_kernel(); 82 lock_kernel(); 83 //初始化time ticket,時(shí)鐘 84 tick_init(); 85 //函數(shù) tick_init() 很簡(jiǎn)單,調(diào)用 clockevents_register_notifier 函數(shù)向 clockevents_chain 通知鏈注冊(cè)元素: 86 // tick_notifier。這個(gè)元素的回調(diào)函數(shù)指明了當(dāng)時(shí)鐘事件設(shè)備信息發(fā)生變化(例如新加入一個(gè)時(shí)鐘事件設(shè)備等等)時(shí), 87 //應(yīng)該執(zhí)行的操作,該回調(diào)函數(shù)為 tick_notify 88 boot_cpu_init(); 89 //初始化頁(yè)地址,當(dāng)然對(duì)于arm這里是個(gè)空函數(shù) 90 page_address_init(); 91 printk(KERN_NOTICE "%s", linux_banner); 92 //系結(jié)構(gòu)相關(guān)的內(nèi)核初始化過(guò)程 93 setup_arch(&command_line); 94 //初始化內(nèi)存管理 95 mm_init_owner(&init_mm, &init_task); 96 //處理啟動(dòng)命令,這里就是設(shè)置的cmd_line 97 setup_command_line(command_line); 98 //這個(gè)在定義了SMP的時(shí)候有作用,現(xiàn)在這里為空函數(shù);對(duì)于smp的使用,后面在看。。。 99 setup_nr_cpu_ids(); 100 //如果沒(méi)有定義CONFIG_SMP宏,則這個(gè)函數(shù)為空函數(shù)。 101 //如果定義了CONFIG_SMP宏,則這個(gè)setup_per_cpu_areas()函數(shù)給每個(gè)CPU分配內(nèi)存, 102 //并拷貝.data.percpu段的數(shù)據(jù)。為系統(tǒng)中的每個(gè)CPU的per_cpu變量申請(qǐng)空間。 103 setup_per_cpu_areas(); 104 //定義在include/asm-x86/smp.h。 105 //如果是SMP環(huán)境,則設(shè)置boot CPU的一些數(shù)據(jù)。在引導(dǎo)過(guò)程中使用的CPU稱(chēng)為boot CPU 106 smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ 107 //設(shè)置node 和 zone 數(shù)據(jù)結(jié)構(gòu) 108 //內(nèi)存管理的講解: 109 build_all_zonelists(NULL); 110 //初始化page allocation相關(guān)結(jié)構(gòu) 111 page_alloc_init(); 112 printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line); 113 //解析內(nèi)核參數(shù) 114 parse_early_param(); 115 parse_args("Booting kernel", static_command_line, __start___param, 116 __stop___param - __start___param, 117 &unknown_bootoption); 118 119 //初始化hash表,以便于從進(jìn)程的PID獲得對(duì)應(yīng)的進(jìn)程描述指針,按照實(shí)際的物理內(nèi)存初始化pid hash表 120 //這里涉及到進(jìn)程管理 121 pidhash_init(); 122 //初始化VFS的兩個(gè)重要數(shù)據(jù)結(jié)構(gòu)dcache和inode的緩存。 123 vfs_caches_init_early(); 124 //把編譯期間,kbuild設(shè)置的異常表,也就是__start___ex_table和__stop___ex_table之中的所有元素進(jìn)行排序 125 sort_main_extable(); 126 //初始化中斷向量表 127 trap_init(); 128 //memory map初始化 129 mm_init(); 130 //核心進(jìn)程調(diào)度器初始化,調(diào)度器的初始化的優(yōu)先級(jí)要高于任何中斷的建立, 131 //并且初始化進(jìn)程0,即idle進(jìn)程,但是并沒(méi)有設(shè)置idle進(jìn)程的NEED_RESCHED標(biāo)志, 132 //所以還會(huì)繼續(xù)完成內(nèi)核初始化剩下的事情。 133 //這里僅僅為進(jìn)程調(diào)度程序的執(zhí)行做準(zhǔn)備。 134 //它所做的具體工作是調(diào)用init_bh函數(shù)(kernel/softirq.c)把timer,tqueue,immediate三個(gè)人物隊(duì)列加入下半部分的數(shù)組 135 sched_init(); 136 //搶占計(jì)數(shù)器加1 137 preempt_disable(); 138 //檢查中斷是否打開(kāi) 139 if (!irqs_disabled()) { 140 printk(KERN_WARNING "start_kernel(): bug: interrupts were " 141 "enabled *very* early, fixing it/n"); 142 local_irq_disable(); 143 } 144 //Read-Copy-Update的初始化 145 //RCU機(jī)制是Linux2.6之后提供的一種數(shù)據(jù)一致性訪問(wèn)的機(jī)制, 146 //從RCU(read-copy-update)的名稱(chēng)上看,我們就能對(duì)他的實(shí)現(xiàn)機(jī)制有一個(gè)大概的了解, 147 //在修改數(shù)據(jù)的時(shí)候,首先需要讀取數(shù)據(jù),然后生成一個(gè)副本,對(duì)副本進(jìn)行修改, 148 //修改完成之后再將老數(shù)據(jù)update成新的數(shù)據(jù),此所謂RCU。 149 rcu_init(); 150 //定義在lib/radix-tree.c。 151 //Linux使用radix樹(shù)來(lái)管理位于文件系統(tǒng)緩沖區(qū)中的磁盤(pán)塊, 152 //radix樹(shù)是trie樹(shù)的一種 153 radix_tree_init(); 154 /* init some links before init_ISA_irqs() */ 155 //early_irq_init 則對(duì)數(shù)組中每個(gè)成員結(jié)構(gòu)進(jìn)行初始化, 156 //例如, 初始每個(gè)中斷源的中斷號(hào).其他的函數(shù)基本為空. 157 early_irq_init(); 158 //初始化IRQ中斷和終端描述符。 159 //初始化系統(tǒng)中支持的最大可能的中斷描述結(jié)構(gòu)struct irqdesc變量數(shù)組irq_desc[NR_IRQS], 160 //把每個(gè)結(jié)構(gòu)變量irq_desc[n]都初始化為預(yù)先定義好的壞中斷描述結(jié)構(gòu)變量bad_irq_desc, 161 //并初始化該中斷的鏈表表頭成員結(jié)構(gòu)變量pend 162 init_IRQ(); 163 //prio-tree是一棵查找樹(shù),管理的是什么? 164 //http://blog.csdn.net/dog250/archive/2010/06/28/5700317.aspx 165 prio_tree_init(); 166 //初始化定時(shí)器Timer相關(guān)的數(shù)據(jù)結(jié)構(gòu) 167 init_timers(); 168 //對(duì)高精度時(shí)鐘進(jìn)行初始化 169 hrtimers_init(); 170 //軟中斷初始化 171 softirq_init(); 172 //初始化時(shí)鐘源 173 timekeeping_init(); 174 //初始化系統(tǒng)時(shí)間, 175 //檢查系統(tǒng)定時(shí)器描述結(jié)構(gòu)struct sys_timer全局變量system_timer是否為空, 176 //如果為空將其指向dummy_gettimeoffset()函數(shù)。 177 time_init(); 178 //profile只是內(nèi)核的一個(gè)調(diào)試性能的工具, 179 //這個(gè)可以通過(guò)menuconfig中的Instrumentation Support->profile打開(kāi)。 180 profile_init(); 181 if (!irqs_disabled()) 182 printk(KERN_CRIT "start_kernel(): bug: interrupts were " 183 "enabled early/n"); 184 //與開(kāi)始的early_boot_irqs_off相對(duì)應(yīng) 185 early_boot_irqs_on(); 186 //與local_irq_disbale相對(duì)應(yīng),開(kāi)中斷 187 local_irq_enable(); 188 gfp_allowed_mask = __GFP_BITS_MASK; 189 //memory cache的初始化 190 kmem_cache_init_late(); 191 //初始化控制臺(tái)以顯示printk的內(nèi)容,在此之前調(diào)用的printk,只是把數(shù)據(jù)存到緩沖區(qū)里, 192 //只有在這個(gè)函數(shù)調(diào)用后,才會(huì)在控制臺(tái)打印出內(nèi)容 193 //該函數(shù)執(zhí)行后可調(diào)用printk()函數(shù)將log_buf中符合打印級(jí)別要求的系統(tǒng)信息打印到控制臺(tái)上。 194 console_init(); 195 if (panic_later) 196 panic(panic_later, panic_param); 197 //如果定義了CONFIG_LOCKDEP宏,那么就打印鎖依賴(lài)信息,否則什么也不做 198 lockdep_info(); 199 200 //如果定義CONFIG_DEBUG_LOCKING_API_SELFTESTS宏 201 //則locking_selftest()是一個(gè)空函數(shù),否則執(zhí)行鎖自測(cè) 202 locking_selftest(); 203 #ifdef CONFIG_BLK_DEV_INITRD 204 if (initrd_start && !initrd_below_start_ok && 205 page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) { 206 printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " 207 "disabling it./n", 208 page_to_pfn(virt_to_page((void *)initrd_start)), 209 min_low_pfn); 210 initrd_start = 0; 211 } 212 #endif 213 //頁(yè)面初始化,可以參考上面的cgroup機(jī)制 214 page_cgroup_init(); 215 //頁(yè)面分配debug啟用 216 enable_debug_pagealloc(); 217 //此處函數(shù)為空 218 kmemtrace_init(); 219 //memory lead偵測(cè)初始化,如何偵測(cè)??? 220 kmemleak_init(); 221 222 //在kmem_caches之后表示建立一個(gè)高速緩沖池,建立起SLAB_DEBUG_OBJECTS標(biāo)志。??? 223 debug_objects_mem_init(); 224 //idr在linux內(nèi)核中指的就是整數(shù)ID管理機(jī)制, 225 //從本質(zhì)上來(lái)說(shuō),這就是一種將整數(shù)ID號(hào)和特定指針關(guān)聯(lián)在一起的機(jī)制 226 //idr機(jī)制適用在那些需要把某個(gè)整數(shù)和特定指針關(guān)聯(lián)在一起的地方。 227 idr_init_cache(); 228 //是否是對(duì)SMP的支持,單核是否需要??這個(gè)要分析 229 setup_per_cpu_pageset(); 230 //NUMA (Non Uniform Memory Access) policy 231 //具體是什么不懂 232 numa_policy_init(); 233 if (late_time_init) 234 late_time_init(); 235 //初始化調(diào)度時(shí)鐘 236 sched_clock_init(); 237 //calibrate_delay()函數(shù)可以計(jì)算出cpu在一秒鐘內(nèi)執(zhí)行了多少次一個(gè)極短的循環(huán), 238 //計(jì)算出來(lái)的值經(jīng)過(guò)處理后得到BogoMIPS 值, 239 //Bogo是Bogus(偽)的意思,MIPS是millions of instructions per second(百萬(wàn)條指令每秒)的縮寫(xiě)。 240 //這樣我們就知道了其實(shí)這個(gè)函數(shù)是linux內(nèi)核中一個(gè)cpu性能測(cè)試函數(shù)。 241 calibrate_delay(); 242 //PID是process id的縮寫(xiě) 243 pidmap_init(); 244 //來(lái)自mm/rmap.c 245 //分配一個(gè)anon_vma_cachep作為anon_vma的slab緩存。 246 //這個(gè)技術(shù)是PFRA(頁(yè)框回收算法)技術(shù)中的組成部分。 247 //這個(gè)技術(shù)為定位而生——快速的定位指向同一頁(yè)框的所有頁(yè)表項(xiàng)。 248 anon_vma_init(); 249 #ifdef CONFIG_X86 250 if (efi_enabled) 251 efi_enter_virtual_mode(); 252 #endif 253 //創(chuàng)建thread_info緩存 254 thread_info_cache_init(); 255 //申請(qǐng)了一個(gè)slab來(lái)存放credentials??????如何理解? 256 cred_init(); 257 //根據(jù)物理內(nèi)存大小計(jì)算允許創(chuàng)建進(jìn)程的數(shù)量 258 fork_init(totalram_pages); 259 //給進(jìn)程的各種資源管理結(jié)構(gòu)分配了相應(yīng)的對(duì)象緩存區(qū) 260 proc_caches_init(); 261 //創(chuàng)建 buffer_head SLAB 緩存 262 buffer_init(); 263 //初始化key的management stuff 264 key_init(); 265 //關(guān)于系統(tǒng)安全的初始化,主要是訪問(wèn)控制 266 security_init(); 267 //與debug kernel相關(guān) 268 dbg_late_init(); 269 //調(diào)用kmem_cache_create()函數(shù)來(lái)為VFS創(chuàng)建各種SLAB分配器緩存 270 //包括:names_cachep、filp_cachep、dquot_cachep和bh_cachep等四個(gè)SLAB分配器緩存 271 vfs_caches_init(totalram_pages); 272 //創(chuàng)建信號(hào)隊(duì)列 273 signals_init(); 274 //回寫(xiě)相關(guān)的初始化 275 page_writeback_init(); 276 #ifdef CONFIG_PROC_FS 277 proc_root_init(); 278 #endif 279 //它將剩余的subsys初始化.然后將init_css_set添加進(jìn)哈希數(shù)組css_set_table[ ]中. 280 //在上面的代碼中css_set_hash()是css_set_table的哈希函數(shù). 281 //它是css_set->subsys為哈希鍵值,到css_set_table[ ]中找到對(duì)應(yīng)項(xiàng).然后調(diào)用hlist_add_head()將init_css_set添加到?jīng)_突項(xiàng)中. 282 //然后,注冊(cè)了cgroup文件系統(tǒng).這個(gè)文件系統(tǒng)也是我們?cè)谟脩?hù)空間使用cgroup時(shí)必須掛載的. 283 //最后,在proc的根目錄下創(chuàng)建了一個(gè)名為cgroups的文件.用來(lái)從用戶(hù)空間觀察cgroup的狀態(tài). 284 cgroup_init(); 285 cpuset_init(); 286 ////進(jìn)程狀態(tài)初始化,實(shí)際上就是分配了一個(gè)存儲(chǔ)線程狀態(tài)的高速緩存 287 taskstats_init_early(); 288 delayacct_init(); 289 //此處為一空函數(shù) 290 imv_init_complete(); 291 //測(cè)試CPU的各種缺陷,記錄檢測(cè)到的缺陷,以便于內(nèi)核的其他部分以后可以使用他們工作。 292 check_bugs(); 293 //電源相關(guān)的初始化 294 acpi_early_init(); /* before LAPIC and SMP init */ 295 // 296 sfi_init_late(); 297 ftrace_init(); 298 //創(chuàng)建1號(hào)進(jìn)程,詳細(xì)分析之 299 rest_init(); 300 } 下面將一些內(nèi)核引導(dǎo)的函數(shù)歸類(lèi),以后查詢(xún)就可以使這個(gè)樣子的,,,這些都是我百度到的,總結(jié)的如下,如果想了解每個(gè)函數(shù)的含義,可以參考一個(gè)文檔,我把鏈接附在下面,http://wenku.baidu.com/link?url=BW4g5AS9KHtvkHorS90lbFOAac2HZFgErLaqeiQr-fejBuRWu8bF28LxKKKxGwGuaQI0ALSUsJeY_dj6m_jgkxX9ozZiE17U0i__sZk_YYa CPU初始化 smp_setup_processor_id() boot_cpu_init() setup_arch(&command_line); setup_nr_cpu_ids() setup_per_cpu_areas() smp_prepare_boot_cpu() setup_per_cpu_pageset();? calibrate_delay(); cpuset_init(); 內(nèi)存管理初始化 boot_init_stack_canary() page_address_init(); mm_init_owner(); page_alloc_init();? mm_init(); rcu_init(); kmem_cache_init_late(); page_cgroup_init();? kmemleak_init();? numa_policy_init(); anon_vma_init(); page_writeback_init(); 進(jìn)程管理 pidhash_init(); sched_init();? sched_clock_init() pidmap_init(); fork_init(totalram_pages);? taskstats_init_early(); 文件系統(tǒng) vfs_caches_init_early();? thread_info_cache_init(); vfs_caches_init(totalram_pages); 中斷 early_irq_init(); init_IRQ();? softirq_init(); 同步互斥 lockdep_init(); lockdep_info(); locking_selftest();? 時(shí)鐘 tick_init();? init_timers();? hrtimers_init(); timekeeping_init(); time_init(); 調(diào)試 debug_objects_early_init(); console_init(); enable_debug_pagealloc();? debug_objects_mem_init(); dbg_late_init(); 其他 sort_main_extable(); trap_init();? efi_enter_virtual_mode(); cred_init();? proc_caches_init();? buffer_init(); key_init();? security_init(); signals_init(); proc_root_init(); delayacct_init(); check_bugs(); acpi_early_init(); sfi_init_late();? 未知 cgroup_init_early(); build_all_zonelists(NULL); preempt_disable(); radix_tree_init(); prio_tree_init(); profile_init(); idr_init_cache(); cgroup_init(); ftrace_init();最后來(lái)總結(jié)一下Linux內(nèi)核的初始化過(guò)程:
- 啟動(dòng)和鎖住內(nèi)核
- 為L(zhǎng)inux的內(nèi)存管理初始化頁(yè)高速緩存和頁(yè)面地址
- 為多CPU做好準(zhǔn)備
- 顯示內(nèi)核標(biāo)志
- 初始化Linux調(diào)度程序
- 分析傳到Linux內(nèi)核的參數(shù)
- 初始化中斷處理程序,定時(shí)器處理程序和信號(hào)處理程序
- 掛載初始文件系統(tǒng)
- 完成系統(tǒng)的初始化,并且將控制權(quán)從init交回系統(tǒng)
小結(jié)
今天主要是描述了系統(tǒng)加電和斷電時(shí)候的內(nèi)核引導(dǎo)期間發(fā)生了什么事情,先討論了BIOS和Firmware以及它們是如何與內(nèi)核引導(dǎo)裝入程序交互的,也討論了裝入程序LILO,GRUB和Yaboot,,最后著重分析了start_kernel()函數(shù)的代碼,這個(gè)是我看網(wǎng)上代碼的,主要是別人分析的太好了,所以借鑒了一下,只要能懂就行,最后列出了一系列的函數(shù),只要看了鏈接上的 都能懂的,,我也努力在看,,共同進(jìn)步吧大家~
?
版權(quán)所有,轉(zhuǎn)載請(qǐng)注明轉(zhuǎn)載地址:http://www.cnblogs.com/lihuidashen/p/4250095.html
posted on 2015-01-27 09:11 NET未來(lái)之路 閱讀(...) 評(píng)論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/lonelyxmas/p/4251897.html
總結(jié)
以上是生活随笔為你收集整理的十天学Linux内核之第七天---电源开和关时都发生了什么的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c#窗体
- 下一篇: MySQL存储引擎InnoDB,MyIS