?? linux啟動(dòng)分析.txt
字號(hào):
引導(dǎo)Linux通常有兩種方法,一種是由MILO及其他類似的引導(dǎo)程序引導(dǎo),另一種是由Firmware直接引導(dǎo)。
“arch/alpha/boot”下就是制作Linux Bootloader的文件。“head.S”文件提供了對(duì) OSF PAL/1的調(diào)用入口,它將被編譯后置于引導(dǎo)扇區(qū)(ARC的分區(qū)首扇區(qū)或SRM的磁盤0扇區(qū)),得到控制后初始化一些數(shù)據(jù)結(jié)構(gòu),再將控制轉(zhuǎn)給“main.c”中的start_kernel(), start_kernel()向控制臺(tái)輸出一些提示,調(diào)用pal_init()初始化PAL代碼,調(diào)用openboot() 打開引導(dǎo)設(shè)備(通過讀取Firmware環(huán)境),調(diào)用load()將核心代碼加載到START_ADDR(見 “include/asm-alpha/system.h”),再將Firmware中的核心引導(dǎo)參數(shù)加載到ZERO_PAGE(0) 中,最后調(diào)用runkernel()將控制轉(zhuǎn)給0x100000的kernel,bootloader部分結(jié)束。
“arch/alpha/boot/bootp.c”以“main.c”為基礎(chǔ),可代替“main.c”與“head.S” 生成用于BOOTP協(xié)議網(wǎng)絡(luò)引導(dǎo)的Bootloader。
Bootloader中使用的所有“srm_”函數(shù)在“arch/alpha/lib/”中定義。
以上這種Boot方式是一種最簡(jiǎn)單的方式,即不需其他工具就能引導(dǎo)Kernel,前提是按照 Makefile的指導(dǎo),生成bootimage文件,內(nèi)含以上提到的bootloader以及vmlinux,然后將 bootimage寫入自磁盤引導(dǎo)扇區(qū)始的位置中。
當(dāng)采用MILO這樣的引導(dǎo)程序來引導(dǎo)Linux時(shí),不需要上面所說的Bootloader,而只需要 vmlinux或vmlinux.gz,引導(dǎo)程序會(huì)主動(dòng)解壓加載內(nèi)核到0x1000(小內(nèi)核)或0x100000(大內(nèi)核),并直接進(jìn)入內(nèi)核引導(dǎo)部分,
bbbbbbbbbbbbbbbbbbbbbbb
arch/alpha/vmlinux.lds的鏈接腳本控制下,鏈接程序?qū)mlinux的入口置于 "arch/alpha/kernel/head.S"中
的__start上,因此當(dāng)Bootloader跳轉(zhuǎn)到0x100000時(shí), __start處的代碼開始執(zhí)行。__start的代碼很簡(jiǎn)單,
只需要設(shè)置一下全局變量,然后就跳轉(zhuǎn)到start_kernel去了。
start_kernel()是"/init/main.c"(體系結(jié)構(gòu)無關(guān))中的asmlinkage函數(shù)。
至此,啟動(dòng)過程轉(zhuǎn)入體系結(jié)構(gòu)無關(guān)的通用C代碼中。
--> 對(duì)arm平臺(tái)
arch/arm/kernel/head-armv.s(32bit)(head-armo.s(26bit))
里面有一句add pc, r10, #12
r10=__proc_info_end(腳本里面定義arch\arm\vmlinux-armo.lds.in(17))
里面包含arm\mm\proc-arm920.S(645)
r10調(diào)整12就是指向結(jié)構(gòu)體里面的一個(gè)一條語句 b __arm920_setup
--<
start_kernel()中調(diào)用了一系列初始化函數(shù),以完成kernel本身的設(shè)置。這些動(dòng)作有的是公共的,有的則是需要配置的才會(huì)執(zhí)行的。
在start_kernel()函數(shù)中,
輸出Linux版本信息(printk(linux_banner))
設(shè)置與體系結(jié)構(gòu)相關(guān)的環(huán)境(setup_arch())
頁表結(jié)構(gòu)初始化(paging_init())
使用"arch/alpha/kernel/entry.S"中的入口點(diǎn)設(shè)置系統(tǒng)自陷入口(trap_init())
使用alpha_mv結(jié)構(gòu)和entry.S入口初始化系統(tǒng)IRQ(init_IRQ())
核心進(jìn)程調(diào)度器初始化(包括初始化幾個(gè)缺省的Bottom-half,sched_init())
時(shí)間、定時(shí)器初始化(包括讀取CMOS時(shí)鐘、估測(cè)主頻、初始化定時(shí)器中斷等,time_init())
提取并分析核心啟動(dòng)參數(shù)(從環(huán)境變量中讀取參數(shù),設(shè)置相應(yīng)標(biāo)志位等待處理,(parse_options())
控制臺(tái)初始化(為輸出信息而先于PCI初始化,console_init())
剖析器數(shù)據(jù)結(jié)構(gòu)初始化(prof_buffer和prof_len變量)
核心Cache初始化(描述Cache信息的Cache,kmem_cache_init())
延遲校準(zhǔn)(獲得時(shí)鐘jiffies與CPU主頻ticks的延遲,calibrate_delay())
內(nèi)存初始化(設(shè)置內(nèi)存上下界和頁表項(xiàng)初始值,mem_init())
創(chuàng)建和設(shè)置內(nèi)部及通用cache("slab_cache",kmem_cache_sizes_init())
創(chuàng)建uid taskcount SLAB cache("uid_cache",uidcache_init())
創(chuàng)建文件cache("files_cache",filescache_init())
創(chuàng)建目錄cache("dentry_cache",dcache_init())
創(chuàng)建與虛存相關(guān)的cache("vm_area_struct","mm_struct",vma_init())
塊設(shè)備讀寫緩沖區(qū)初始化(同時(shí)創(chuàng)建"buffer_head"cache用戶加速訪問,buffer_init())
創(chuàng)建頁cache(內(nèi)存頁hash表初始化,page_cache_init())
創(chuàng)建信號(hào)隊(duì)列cache("signal_queue",signals_init())
初始化內(nèi)存inode表(inode_init())
創(chuàng)建內(nèi)存文件描述符表("filp_cache",file_table_init())
檢查體系結(jié)構(gòu)漏洞(對(duì)于alpha,此函數(shù)為空,check_bugs())
SMP機(jī)器其余CPU(除當(dāng)前引導(dǎo)CPU)初始化(對(duì)于沒有配置SMP的內(nèi)核,此函數(shù)為空,smp_init())
啟動(dòng)init過程(創(chuàng)建第一個(gè)核心線程,調(diào)用init()函數(shù),原執(zhí)行序列調(diào)用cpu_idle() 等待調(diào)度,rest_init())
至此start_kernel()結(jié)束,基本的核心環(huán)境已經(jīng)建立起來了。
/init/main.c中
init()函數(shù)作為核心線程,首先鎖定內(nèi)核(僅對(duì)SMP機(jī)器有效),
然后調(diào)用 do_basic_setup()完成外設(shè)及其驅(qū)動(dòng)程序的加載和初始化。過程如下:
do_basic_setup:
總線初始化(比如pci_init())
網(wǎng)絡(luò)初始化(初始化網(wǎng)絡(luò)數(shù)據(jù)結(jié)構(gòu),包括sk_init()、skb_init()和proto_init()三部分,在proto_init()中,將調(diào)用protocols結(jié)構(gòu)中包含的所有協(xié)議的初始化過程,sock_init())
創(chuàng)建bdflush核心線程(bdflush()過程常駐核心空間,由核心喚醒來清理被寫過的內(nèi)存緩沖區(qū),當(dāng)bdflush()由kernel_thread()啟動(dòng)后,它將自己命名為kflushd)
創(chuàng)建kupdate核心線程(kupdate()過程常駐核心空間,由核心按時(shí)調(diào)度執(zhí)行,將內(nèi)存緩沖區(qū)中的信息更新到磁盤中,更新的內(nèi)容包括超級(jí)塊和inode表)
設(shè)置并啟動(dòng)核心調(diào)頁線程kswapd(為了防止kswapd啟動(dòng)時(shí)將版本信息輸出到其他信息中間,核心線調(diào)用kswapd_setup()設(shè)置kswapd運(yùn)行所要求的環(huán)境,然后再創(chuàng)建 kswapd核心線程)
創(chuàng)建事件管理核心線程(start_context_thread()函數(shù)啟動(dòng)context_thread()過程,并重命名為keventd)
設(shè)備初始化(包括并口parport_init()、字符設(shè)備chr_dev_init()、塊設(shè)備 blk_dev_init()、SCSI設(shè)備scsi_dev_init()、網(wǎng)絡(luò)設(shè)備net_dev_init()、磁盤初始化及分區(qū)檢查等等,device_setup())
執(zhí)行文件格式設(shè)置(binfmt_setup())
啟動(dòng)任何使用__initcall標(biāo)識(shí)的函數(shù)(方便核心開發(fā)者添加啟動(dòng)函數(shù),do_initcalls())
文件系統(tǒng)初始化(filesystem_setup())
//安裝root文件系統(tǒng)(mount_root())
至此do_basic_setup()函數(shù)返回init(),在釋放啟動(dòng)內(nèi)存段(free_initmem())并給內(nèi)核解鎖以后,init()打開/dev/console設(shè)備,重定向stdin、stdout和stderr到控制臺(tái),最后,搜索文件系統(tǒng)中的init程序(或者由init=命令行參數(shù)指定的程序),并使用 execve()系統(tǒng)調(diào)用加載執(zhí)行init程序。
prepare_namespace
init()函數(shù)到此結(jié)束,內(nèi)核的引導(dǎo)部分也到此結(jié)束了,這個(gè)由start_kernel()創(chuàng)建的第一個(gè)線程已經(jīng)成為一個(gè)用戶模式下的進(jìn)程了。此時(shí)系統(tǒng)中存在著六個(gè)運(yùn)行實(shí)體:
start_kernel()本身所在的執(zhí)行體,這其實(shí)是一個(gè)"手工"創(chuàng)建的線程,它在創(chuàng)建了init()線程以后就進(jìn)入cpu_idle()循環(huán)了,它不會(huì)在進(jìn)程(線程)列表中出現(xiàn)
init線程,由start_kernel()創(chuàng)建,當(dāng)前處于用戶態(tài),加載了init程序
kflushd核心線程,由init線程創(chuàng)建,在核心態(tài)運(yùn)行bdflush()函數(shù)
kupdate核心線程,由init線程創(chuàng)建,在核心態(tài)運(yùn)行kupdate()函數(shù)
kswapd核心線程,由init線程創(chuàng)建,在核心態(tài)運(yùn)行kswapd()函數(shù)
keventd核心線程,由init線程創(chuàng)建,在核心態(tài)運(yùn)行context_thread()函數(shù)
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -