Linu內(nèi)核解析資料_第1頁
Linu內(nèi)核解析資料_第2頁
Linu內(nèi)核解析資料_第3頁
Linu內(nèi)核解析資料_第4頁
Linu內(nèi)核解析資料_第5頁
已閱讀5頁,還剩255頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Linux內(nèi)核解析

IBootstrap

1匯編代碼分析

2start_kernel函數(shù)

3準(zhǔn)備進(jìn)入用戶態(tài)

3.1Initrd初始化

3.1.0準(zhǔn)備知識(shí)

在講述如何釋放initrd到rootfs之前,有比較講述一下什么是rootfs,rootfs的初始化相關(guān)的函數(shù);以及rootfs

的初始化函數(shù)是如何被調(diào)用的。

這里所說的rootfs指的是VFS的根節(jié)點(diǎn)/,以及在內(nèi)存中創(chuàng)建的根目錄/下的文件和目錄節(jié)點(diǎn),這個(gè)文件系

統(tǒng)僅僅存在于內(nèi)存之中,由內(nèi)核初始化的時(shí)候負(fù)責(zé)創(chuàng)建,該文件系統(tǒng)不會(huì)存儲(chǔ)到其它非易失性介質(zhì)上。該

rootfs文件系統(tǒng)mntinit函數(shù)調(diào)用initrootfs和initmounttree兩個(gè)函數(shù)來負(fù)責(zé)創(chuàng)建和初始化:

void_initmnt_init(void)

〃這個(gè)函數(shù)很簡(jiǎn)單,就是注冊(cè)了rootfs的文件系統(tǒng)。

init_rootfs();

〃在這里,將rootfs文件系統(tǒng)掛載,它的掛載點(diǎn)默認(rèn)為。

〃最后切換進(jìn)程的根目錄和當(dāng)前目錄為,這也就是根目錄的由來。

〃不過這里只是初始化,等掛載完具體的文件系統(tǒng)之后,

〃一般都會(huì)將根目錄切換到具體的文件系統(tǒng),所以在系統(tǒng)啟動(dòng)之后,

〃用mount命令是看不到rootfs的掛載信息的。

initmounttree();

有了rootfs后,就可以將initrd的image釋放到rootfs中了,至于哪個(gè)函數(shù)完成這項(xiàng)工作?在講述該函數(shù)之

前,我們首先看看該函數(shù)是如何被調(diào)用的。首先看kernelinit函數(shù)中的dobasicsetup函數(shù):

staticint_initkernel_init(void*unused)

do_basic_setup();

dobasicsetup()是?個(gè)很關(guān)鍵的函數(shù),所有直接編譯在kernel中的模塊都是由它啟動(dòng)的。

*Ok,themachineisnowinitialized.Noneofthedevices

*havebeentouchedyet,buttheCPUsubsystemisupand

*running,andmemoryandprocessmanagementworks.

*

*Nowwecanfinallystartdoingsomerealwork..

*/

〃注意上面的關(guān)于該函數(shù)的注釋:CPU和進(jìn)程管理模塊已經(jīng)正常工作,但是外設(shè)還沒初始化。

staticvoid_initdo_basic_setup(void)

{

cpuset_init_smp();

usermodehelper_init();

init_tmpfs();

driver_init();

init_irq_proc();

do_ctors();

〃啟動(dòng)所有在—initcallstart和—initcallend段的函數(shù),

//而靜態(tài)編譯進(jìn)內(nèi)核的modules也會(huì)將其入口放置在這段區(qū)間里。

do_initcalls();

將initrd的image釋放至rootfs中的工作是由populate_rootfs函數(shù)完成,該函數(shù)由rootfs_initcall()所引用。

注意到有以下初始化函數(shù):

rootfsinitcall(populaterootfs);

如此:也就是說會(huì)在系統(tǒng)初始化的時(shí)候,也就是do_initcalls被調(diào)用的時(shí)候,會(huì)調(diào)用populate_rootfs進(jìn)行初

始化。

3.1.1釋放initrd

總的來說,rootfs分為兩種:虛擬rootfs和真實(shí)rootfs。現(xiàn)在kernel的發(fā)展趨勢(shì)是將更多的功能放到用戶

空間完成,以保持內(nèi)核的精簡(jiǎn)。虛擬rootfs也是各linux發(fā)行廠商普遍采用的一種方式,可以將一部份的

初始化工作放在虛擬的rootfs里完成,然后切換到真實(shí)的文件系統(tǒng)。在虛擬rootfs的發(fā)展過程中,又有以

下幾個(gè)版本:

Initramfs:Initramfs是在kernel2.5中引入的技術(shù),實(shí)際上它的含義就是:在內(nèi)核鏡像中附加一個(gè)cpio包,

這個(gè)cpio包中包含了一個(gè)小型的文件系統(tǒng),當(dāng)內(nèi)核啟動(dòng)時(shí),內(nèi)核將這個(gè)cpio包解開,并且將其中包含的

文件系統(tǒng)釋放到rootfs中,內(nèi)核中的一部分初始化代碼會(huì)放到這個(gè)文件系統(tǒng)中,作為用戶層進(jìn)程來執(zhí)行。

這樣帶來的明顯的好處是精簡(jiǎn)了內(nèi)核的初始化代碼,而且使得內(nèi)核的初始化過程更容易定制。這種這種方

式的rootfs是包含在kernelimage之中的。

cpio-initrd:cpio格式的rootfs

image-initrd:傳統(tǒng)格式的rootfs

將在下文具體講述這兩種initrd。

populaterootfs代碼如下:

*處理Initramfs/cpio-initrd/image-initrd三個(gè)格式的image。

*主要功能實(shí)現(xiàn)了將Initramfs/cpio-initrd兩種格式的image釋放到rootfs;

*不同不出image-initrd這種老式的initrd格式,需要依賴其它手段,后續(xù)會(huì)講。

*/

staticint_initpopulate_rootfs(void)

*第一種情況是跟kernel融為一體的initramfs,從內(nèi)核2.5引入這種技術(shù)。在編譯kernel的時(shí)候,

通過鏈接腳本

*將其存放在—initramfs_start至—initramfs_end的區(qū)域。這種情況下,

*直接調(diào)用unpack_to_rootfs將其釋放到根目錄/。

*

*如果不是屬于initramfs這種形式的,也就是_initramfs_start和_initramfs_end的值相等,

*長(zhǎng)度為零,不會(huì)做任何處理,退出;err此時(shí)也為NULL。

*/

char*err=unpack_to_rootfs(_initramfs_start,_initramfs_size);

if(err)

panic(err);/*FailedtodecompressINTERNALinitramfs*/

*緊接著處理initrd格式,而initrd又有兩種格式:Image?initrd和CPIO-initrdo

*由initrd_start變量是否為0,來判斷內(nèi)核是否接受了參數(shù):“initrd=",如果有參數(shù)“initrd=",

*由下面的代碼對(duì)initrd進(jìn)行處理,處理過程又根據(jù)initrd的兩種格式而不盡相同:

*CPIO-initrd:會(huì)被直接釋放到rootfs;

*Image-initrd:暫時(shí)不會(huì)被釋放到rootfs,而是將initrdimage拷貝到/initrd.image文件中。

*/

if(initrd_start){

/*

*必須要配制CONFIG_BLK_DEV_RAM才會(huì)支持image?initrd。否則全當(dāng)成cpio-initrd的形式

處理。

*/

#iftlefCONFIG_BLK_DEV_RAM

intfd;

printk(KERN_INFO"Tryingtounpackrootfsimageasinitramfs...\nu);

err=unpack_to_rootfs((char*)initrd_start,initrd_end-initrdstart);

//如果unpack_to_rootfs返回正常

if(!err){

free_initrd();

return0;

}else{

〃如果釋放initrd到根目錄發(fā)生錯(cuò)誤,重新釋放initramfso

clean_rootfs();

unpack_to_rootfs(_initramfs_start,_initramfs_size);

}

printk(KERN_INFO"rootfsimageisnotinitramfs(%s)n

lookslikeaninitrd\nH,err);

fd=sys_open((constchar_user_force*)"/initrd.image”,O_WRONLY|O_CREAT,0700);

/*

*將initrd寫入/initrd.image文件

*看上去image-initrd和cpio-initrd兩種格式的image都會(huì)被寫入/initrd.image

*這看上去不太合理,因?yàn)閡npack_to_rootfs能夠正確處理cpio-initrd,就不需要

*拷貝到/initrd.image了;其實(shí)內(nèi)核是根據(jù)是否配置CONFIG_BLK_DEV_RAM來

*判斷是否支持image-initrd,如果不支持image-initrd,就不要配置CONFIG_BLK_DEV_RAM,

*內(nèi)核就會(huì)編譯下面的宏定義段內(nèi)代碼,就不會(huì)拷貝initrd到/initdimage了。

*/

if(fd>=0){

sys_write(f(i,(char*)initrd_start,initrd_end-initrd_start);

sys_close(fci);

/*

*對(duì)于是image?initrd的情況。將其釋放到/initrd.image.

*最后將initrd內(nèi)存區(qū)域歸入伙伴系統(tǒng)。這段內(nèi)存就可以由操作系統(tǒng)來做其它的用途了。

*/

free_initrd();

)

#else

printk(KERN_INFO"Unpackinginitramfs...\nu);

err=unpacktorootfs((char*)initrdstart,

initrdend-initrd_start);

if(err)

printk(KERN_EMERGnInitramfsunpackingfailed:%s\n”,err);

free_initrd();

#endif

}

return0;

}

rootfsinitcall(populaterootfs);

*正確,返回NULL;否則返回一個(gè)C語言格式字符串。

*

*顧名思義就是解壓包,并將其釋放至rootfs。

*在這個(gè)函數(shù)里,對(duì)應(yīng)如下的三種虛擬根文件系統(tǒng)的情況:

*Initramfs是在kernel2.5中引入的技術(shù),實(shí)際上它的含義就是:在內(nèi)核鏡像中附加一個(gè)cpio包,這個(gè)cpio

*包中包含了一個(gè)小型的文件系統(tǒng),當(dāng)內(nèi)核啟動(dòng)時(shí),內(nèi)核將這個(gè)cpio包解開,并且將其中包含的文件系

*統(tǒng)釋放到rootfs中,內(nèi)核中的一部分初始化代碼會(huì)放到這個(gè)文件系統(tǒng)中,作為用戶層進(jìn)程來執(zhí)行。這樣

*帶來的明顯的好處是精簡(jiǎn)了內(nèi)核的初始化代碼,而且使得內(nèi)核的初始化過程更容易定制。這種這種方

*式的rootfs是包含在kernelimage之中的.

*cpio-initrd:cpio格式的rootfs

*image-initrd:傳統(tǒng)格式的rootfs

*關(guān)于這兩種虛擬文件系統(tǒng),請(qǐng)參考后續(xù)章節(jié)。

*

*

*一種是跟kernel融為一體的initramfs,在編譯kernel的時(shí)候,通過鏈接腳本

*將其存放在—initramfs_start至—initramfs_end的區(qū)域。這種情況下,

*直接調(diào)用unpack_to_rootfs將其釋放到根目錄。

*如果不是屬于這種形式的,也就是_initramfs_start和__initramfs_end的值相等,

*長(zhǎng)度為零。不會(huì)做任何處理,退出。

*對(duì)應(yīng)后兩種情況,image?initrd不會(huì)被釋放到rootfs,該格式的處理細(xì)節(jié)后文繼續(xù)講解;

*也就是說unpack_to_rootfs不能正確處理image-initrd,需要依賴其它手段。

*對(duì)于是cpio?initrd的情況,直接將其釋放到根目錄。

*/

staticchar*_initunpack_to_rootfs(char*buf,unsignedlen)

(

intwritten,res;

decompress_fhdecompress;

constchar*compress_name;

static_initdatacharmsg_buf[64];

header_buf=kmalloc(110,GFP_KERNEL);

symlink_buf=kmalloc(PATH_MAX+N_ALIGN(PATH_MAX)+1,GFP_KERNEL);

name_buf=kmalloc(N_ALIGN(PATH_MAX),GFP_KERNEL);

if(!header_buf||Jsymlinkbuf||!name_buf)

panic(ncan,tallocatebuffers");

state=Start;

thisheader=0;

message=NULL;

while(!message&&len){

loff_tsaved_oflset=thisheader;

if(*buf==O&&!(this_header&3)){

state=Start;

written=write_buffer(buf,len);

buf+=written;

len-=written;

continue;

)

if(!*buf){

bufH-;

len—;

this_header++;

continue;

)

this_header=0;

decompress=decompressmethodCbui;len,&compress_name);

if(decompress){

res=decompress(buf,len,NULL,flushbuffer,NULL,

&my_inptr,error);

if(res)

error(ndecompressorfailed");

}elseif(compress_name){

if(!message){

snprintf{msg_buf,sizeofmsgbuf,

"compressionmethod%snotconfigured*1,

compressname);

message=msg_buf;

}

}else

error(njunkincompressedarchive1*);

if(state!=Reset)

error(njunkincompressedarchive");

this_header=saved_offset+my_inptr;

buf+=myinptr;

len-=my_inptr;

dirutime();

kfree(name_buf);

kfree(symlink_buf);

kfree(headerbuf);

returnmessage;

3.2initrd

到目前為止,initrd技術(shù)雖然是一種比較過時(shí)但還是一種比較常用的技術(shù)。LinuxKemel2.6的initrd的文件

系統(tǒng)鏡像文件變成了叩i。格式;變化不僅僅體現(xiàn)在文件格式上,內(nèi)核對(duì)這兩個(gè)格式的initrd有著截然不同

的處理方法。本節(jié)首先介紹什么是initrd技術(shù),然后介紹了3.0內(nèi)核的initrd的處理流程。

Initrd的含義是bootloaderinitializedRAMDisk.就是由bootloader初始化的內(nèi)存盤。在Linux內(nèi)核啟動(dòng)之

前,bootloader會(huì)將存儲(chǔ)介質(zhì)中的initrd文件加載到內(nèi)存,內(nèi)核啟動(dòng)時(shí)會(huì)在加載rootfs之前首先訪問內(nèi)存中

的initrd文件系統(tǒng)。在bootloader配置了initrd的情況下,內(nèi)核啟動(dòng)被分為兩個(gè)階段:第一階段首先執(zhí)行initrd

中的某個(gè)文件(/linuxrc或者/init,下文會(huì)詳細(xì)講),文成一些特定的任務(wù),如加載RAID驅(qū)動(dòng)等;第二階

段才是真正執(zhí)行rootfs中的/sbin/init,以啟動(dòng)init進(jìn)程。

Initrd主要有以下四個(gè)優(yōu)勢(shì):

1動(dòng)態(tài)適應(yīng)底層硬件平臺(tái):Linux發(fā)行版必須支持各種各樣的底層平臺(tái),將所有的驅(qū)動(dòng)都編譯進(jìn)內(nèi)核是不

太現(xiàn)實(shí)的,而且內(nèi)核會(huì)常駐內(nèi)存,這也會(huì)造成內(nèi)存資源的極大浪費(fèi)。Linux發(fā)行版的內(nèi)核中只會(huì)包含最基

本的一些驅(qū)動(dòng);而通過在安裝過程中檢測(cè)系統(tǒng)硬件,動(dòng)態(tài)生成底層平臺(tái)依賴的initrd無非是一種既可行又

靈活的解決方案。

2用于LiveCD:LiveCD可能面對(duì)復(fù)雜的硬件環(huán)境,所以也必須使用initrd。

3制作USB啟動(dòng)盤時(shí)必須使用initrd:USB設(shè)備是一個(gè)啟動(dòng)比較慢的設(shè)備,從驅(qū)動(dòng)加載到設(shè)備真正可用大

概需要幾秒的時(shí)間。如果將USB驅(qū)動(dòng)編譯進(jìn)內(nèi)核,內(nèi)核通常不能正確訪問USB中的文件系統(tǒng),因?yàn)閮?nèi)核

訪問USB時(shí);USB設(shè)備還沒有初始化完畢。所以通常的做法是:在initrd中加載USB驅(qū)動(dòng),然后休眠幾

秒鐘,等待USB設(shè)備初始化完畢后再去掛載USB中的文件系統(tǒng)。

4initrd(腳本)很靈活,而且是工作在用戶態(tài),可以在其中很方便的定制一些功能,如系統(tǒng)安全相關(guān)的設(shè)

fio

在深入介紹內(nèi)核2.6及其以后版本使用的initrd機(jī)制之前,先簡(jiǎn)單介紹內(nèi)核2.4中的initrd機(jī)制。內(nèi)核2.4

中的initrd的文件格式是一個(gè)文件系統(tǒng)鏡像文件,姑且稱之為image-initrd,以區(qū)分內(nèi)核2.6及其以后的CPIO

格式的initrdo內(nèi)核2.4對(duì)initrd的處理流程如下:

1bootloader將內(nèi)核和initrd兩個(gè)image加載到內(nèi)存,經(jīng)過一些必要的初始化后,將控制權(quán)交給內(nèi)核;

2內(nèi)核經(jīng)過一些進(jìn)一步的初始化后,將initrd所在的內(nèi)存區(qū)域(由bootloader告知內(nèi)核)設(shè)置為/dev/initrd

設(shè)備;然后kernel透過內(nèi)部的decompressor(gzip解壓縮)解開該內(nèi)容并復(fù)制到/dev/ramO裝置設(shè)備上;

3內(nèi)核以R/W(可讀寫)模式將/dev/ramO掛載為暫時(shí)性的rootfs;

4kernel準(zhǔn)備執(zhí)行/dev/ramO上的/linuxrc,該程序(腳本)是在用戶態(tài)執(zhí)行的;如果/dev/ramO被指定

為真正的根文件系統(tǒng),那么內(nèi)核跳至第7步正常啟動(dòng);

5/linuxrc與相關(guān)程序處理特定的操作;linuxrc通常是一個(gè)腳本文件,負(fù)責(zé)加載內(nèi)核訪問根文件系統(tǒng)必須

的驅(qū)動(dòng),以及加載根文件系統(tǒng);

6/linuxrc執(zhí)行即將完畢,執(zhí)行權(quán)轉(zhuǎn)交給kernel;如果rootfs中存在/initrd目錄,那么/dev/ramO將從/移

動(dòng)至lj/initrdo否則如果/initrd目錄不存在,/dev/ramO將被卸載。

7Linux掛載真正的rootfs并執(zhí)行/sbin/init程式

8依據(jù)Linuxdistribution規(guī)范的流程,執(zhí)行各式系統(tǒng)與應(yīng)用程式;linux2.4內(nèi)核的initrd的執(zhí)行是作為內(nèi)

核啟動(dòng)的一個(gè)中間階段,也就是說initrd的/linuxrc執(zhí)行以后,內(nèi)核會(huì)繼續(xù)執(zhí)行初始化代碼,我們后面會(huì)

看到這是linux2.4內(nèi)核同2.6內(nèi)核的initrd處理流程的一個(gè)顯著區(qū)別。

內(nèi)核2.6支持兩種格式的initrd,一種是前面提到的傳統(tǒng)格式個(gè)image-initrd,其核心文件是/linuxrc;另外

一種格式是CPIO,這種格式從內(nèi)核2.5開始引入,使用CPIO工具生成,其核心文件是/init,姑且稱這種

initrd為CPIO-initrdo盡管內(nèi)核2.6對(duì)image-initrd和CPIO-initrd都有支持,但處理流程卻有著顯著的區(qū)別。

下面分別介紹內(nèi)核2.6對(duì)這兩種格式的initrd的處理流程:

CPIO-initrd的處理流程:

1Bootloader將內(nèi)核initrd加載到內(nèi)存特定位置;

2內(nèi)核判斷initrd是否是CPIO格式,如果是:

3將initrd的內(nèi)容釋放到rootfs中;

4執(zhí)行initrd中的/init文件;

5執(zhí)行完/init后,控制權(quán)再交給內(nèi)核

Image-initrd的處理流程:

1Bootloader將內(nèi)核initrd加載內(nèi)存特定位置;

2內(nèi)核判斷initrd格式,如果不是CPIO格式,則作為Image-initrd格式處理;

3將initrd的內(nèi)容保存在rootfsft\]/initrd.image文件中;

4將/initrd.image中的內(nèi)容讀入到/dev/ramO中,也就是讀入了一個(gè)內(nèi)存盤中;

5接著內(nèi)核以讀寫的方式將/dev/ramO掛載原始的根文件系統(tǒng);

6如果/dev/ramO被指定為rootfs,跳轉(zhuǎn)到最后一步進(jìn)行正常啟動(dòng);

7執(zhí)行/linuxrc文件,linuxrc通常是一個(gè)腳本,負(fù)責(zé)加載根文件需要的驅(qū)動(dòng),以及加載根文件系統(tǒng);

8/linuxrc執(zhí)行完畢,控制權(quán)交給內(nèi)核,常規(guī)跟文件系統(tǒng)被掛載;

9如果常規(guī)根文件系統(tǒng)中存在/initrd目錄,那么/dev/ramO將從/移動(dòng)到/initrd;否則如果/initrd不存在,

/dev/ramO將被卸載;

10在rootfs上進(jìn)行啟動(dòng),執(zhí)行/sbin/init。

通過上面的流程可知,內(nèi)核2.6對(duì)image-initrd的處理流程相較內(nèi)核2.4并無明顯變化;CPIO-image相較

image?initrd有很大差別,流程非常簡(jiǎn)單。

CPIO-image相較image-initrd,有一些區(qū)別和優(yōu)勢(shì):

1CPIO-initrd的制作比較簡(jiǎn)單,通過以下兩個(gè)命令即可完成:

#假設(shè)當(dāng)前目錄位于準(zhǔn)備好的initrd文件系統(tǒng)的根目錄下

bash#find.|cpio-c-o>../initrd.img

bash#gzip../initrd.img

而image?initrd的制作相對(duì)繁瑣:

#假設(shè)當(dāng)前目錄位于準(zhǔn)備好的initrd文件系統(tǒng)的根目錄下

bash#ddif=/dev/zeroof=../initrd.imgbs=512kcount=5

bash#mkfs.ext2-F-mO../initrd.img

bash#mount-text2-oloop../initrd.img/mnt

bash#cp-r*/mnt

bash#umount/mnt

bash#gzip-9../initrd.img

2CPIO-image的內(nèi)核處理流程簡(jiǎn)單

通過上面的對(duì)CPIO-initrd處理流程的介紹,CPIO-initrd的處理流程也顯得格外簡(jiǎn)單,CPIO-initrd主要在以

下兩個(gè)方面做了簡(jiǎn)化:

aCPIO-image并沒有使用額外的RamDisk,這樣就省略了RamDisk的掛載和卸載工作;

bCPIOinitrd啟動(dòng)完/init進(jìn)程,內(nèi)核任務(wù)就結(jié)束了,剩下的工作都交給/init處理;而對(duì)于image?initrd,內(nèi)

核在執(zhí)行完/linuxrc之后,還需要等待其結(jié)束,再進(jìn)行一些收尾工作,并且需要負(fù)責(zé)執(zhí)行rootfs中的/sbin/init。

可以通過下面的簡(jiǎn)化的流程圖更加清晰表示兩者之間的區(qū)別:

內(nèi)核對(duì)cpio-initrd和image-initrd處理流程示意圖

image-mitrd的處理階段cpio-initrd的處理階段

CPIO-initrd的職能更加重要,不再像image-initrd那樣是Linux啟動(dòng)過程的一個(gè)中間步驟,而是作為內(nèi)核啟

動(dòng)的終極步驟,內(nèi)核將控制權(quán)交給/init后就完了,所以CPIO-initrd可以做更多的工作,而不需要擔(dān)心和內(nèi)

核后續(xù)處理的銜接問題。

3.2.2內(nèi)核2.6Initrd處理代碼分析

下面對(duì)內(nèi)核2.6中與initrd相關(guān)的代碼進(jìn)行詳細(xì)分析。首先闡述幾個(gè)相關(guān)的概念;

rootfs:一個(gè)駐留在內(nèi)存中的文件系統(tǒng),也是Linux初始化時(shí)構(gòu)建的第一個(gè)文件系統(tǒng),其實(shí)VFS。

initramfs:和本文的主題關(guān)系不是太大,但是代碼涉及到了initramfs,為了更好地理解代碼,對(duì)其做簡(jiǎn)單

介紹。Initramfs是內(nèi)核2.5引入的技術(shù),它的實(shí)際含義是,內(nèi)核鏡像中附加一個(gè)CPIO包,該CPIO包中包

含了一個(gè)小型的文件系統(tǒng),當(dāng)內(nèi)核啟動(dòng)的時(shí)候,內(nèi)核將包解開,并且將其中包含的文件系統(tǒng)釋放到rootfs

中。內(nèi)核中的一部分初始化代碼會(huì)放到這個(gè)文件系統(tǒng)中,作為用戶態(tài)程序來運(yùn)行。這樣明顯的好處是簡(jiǎn)化

了內(nèi)核的初始化代碼,而且使得內(nèi)核的初始化過程更加容易定制。Linux內(nèi)核2.6.12中的initramfs還沒有

什么實(shí)質(zhì)性的內(nèi)容,一個(gè)完整的initramfs的實(shí)現(xiàn)還需要一個(gè)漫長(zhǎng)的過程。

realfs:用戶最終使用文件系統(tǒng)。

內(nèi)核初始化代碼起始于start_kemeh和initrd相關(guān)的代碼起始于kernelinit,下圖列出了跟該initrd相關(guān)的

函數(shù)調(diào)用關(guān)系:

日?Akernel_execve(constcharconstchar*const*,constchar*const*):int

國(guó)?*___call_usermodehelper(void*):int

?{init_ksymtab_kernel_execve}0:constkernel_symbol

B?:dojinuxrc(void*):int

E)?:handleJnitrdO:void

B?AinitrdJoadO:int

Q?一prRpa「R,amRspacR()voidImage-initrd

pcernel_init(void*):int

B?:rest_initO:void

?start_kernelO:void

日run_initjxocess(constchar*):void

Binitjx)stOint(6matches)CPIO-initrd

日kernel_init(void*):int

臼?:restJnitO:void

?start_kemelO:void

首先從kernel)正函數(shù)開始分析,我們省略了該函數(shù)中跟initrd無關(guān)的代碼。一

staticint_initkemel_init(void*unused)

{

〃省略跟initrd無關(guān)的部分

if(!ramdisk_execute_coininand)

ramdisk_execute_command="/init";

if(sys_access((constchar_user*)ramdisk_execute_command,0)!=0){

〃返回值!=0,表示不存在該文件;所以就會(huì)使用老式的方法去執(zhí)行/linuxrc,也就是傳統(tǒng)的initrd

格式

ramdiskexecutecommand=NULL;

preparenamespace();

*Ok,wehavecompletedtheinitialbootup,and

*we'reessentiallyupandrunning.Getridofthe

*initmemsegmentsandstarttheuser-modestuff..

*/

init_post();

return0;

從上面的代碼可知,根據(jù)rootfs中是否存在ramdisk_execute_command文件,HP/init文件,處理流程分支

為Image-initrd和GPIO-initrdo

GPIO-initrd處理

我們首先看看GPIO-initrd啟動(dòng)的完整代碼,有上面介紹的kemelinit函數(shù)可知,該過程從init_post函數(shù)開

始:

staticnoinlineintinit_post(void)

(

〃省略跟initrd無關(guān)的代碼

〃假如變量ramdisk_execute_command被設(shè)置;

//ramdisk_execute_command一般在函數(shù)kemelinit被設(shè)置為/init

if(ramdisk_execute_command){

run_init_process(ramdisk_execute_command);

printk(KERN_WARNING"Failedtoexecute%s\nn,

ramdisk_execute_command);

*Wetryeachoftheseuntilonesucceeds.

*

*TheBourneshellcanbeusedinsteadofinitifweare

*tryingtorecoverareallybrokenmachine.

*/

〃由于一些歷史原因,有一些不同的init程序文件存在,我們會(huì)依次測(cè)試每一種合理的

〃可能性;如果run_init_process執(zhí)行某個(gè)文件成功,則不會(huì)返回了,也就從內(nèi)核狀態(tài)

〃調(diào)用用戶態(tài)程序,然后返回到了用戶態(tài)。關(guān)于細(xì)節(jié)請(qǐng)看runjnit_process函數(shù)的實(shí)現(xiàn)。

if(execute_command){

runinitprocess(executecommand);

printk(KERN_WARNING"Failedtoexecute%s.Attempting

ndefaults...\n",execute_command);

}

run_init_process(n/sbin/initu);

run_init_process(*7etc/initn);

run_init_process(*7bin/init");

run_init_process(n/bin/shu);

panic("Noinitfound.Trypassinginit=optiontokernel.n

"SeeLinuxDocumentation7init.txtforguidance.**);

runinitprocess函數(shù)定義如下,該函數(shù)只是一個(gè)對(duì)kemelexecve函數(shù)的簡(jiǎn)單wrap。

staticvoidrun_init_process(constchar*init_filename)

{

argv_init[O]=init_filename;

kernel_execve(init_filename,argv_init,envp_init);

kernelexecve函數(shù)的定義如下:

intkemel_execve(constchar*filename,

constchar*constargv[],

constchar*constenvp[])

{

structptregsregs;

intret;

memset(®s,0,sizeofifstructpt_regs));

//加載并執(zhí)行一個(gè)新的可執(zhí)行程序

ret=doexecve(fi1ename,

(constchar_user*const_user*)argv,

(constchar_user*const_user*)envp,®s);

if(ret<0)

gotoout;

/*

*Saveargctotheregisterstructurefbruserspace.

*/

〃將doexecve的返回值存入regs.ARMrO;

〃因?yàn)锳RM規(guī)定,使用RO返回函數(shù)的執(zhí)行結(jié)果。

regs.ARMrO=ret;

*Weweresuccessful.Wewon'tbereturningtoourcaller,but

*insteadtouserspacebymanipulatingthekernelstack.

*/

asm("addrO,%0,%l\n\t"

Hmovrl,%2\n\tH

Hmovr2,%3\n\t”

“blmemmove\n\t,7*copyregstotopofstack*/

"movr8,#0\n\t"/*notasyscall*/

Hmovr9,%0\n\tu/*threadstructure*/

"movsp,r0\n\t*7*repositionstackpointer*/

“bret_to_usern

:nr"(current_thread_infb()),

"Irn(THREAD_START_SP-sizeofifregs)),

nr"(®s),

nIrn(sizeof(regs))

:“r2“JipT'hV'memory");

out:

returnret;

}

EXPORTSYMBOL(kernelexecve);

Image-initrd處理

為處理Image-initrd,首先將ramdisk_execute_command設(shè)置為NULL,然后調(diào)用preparenamespace函數(shù),

如下面的代碼所示:

if(sys_access((constchar_user*)ramdisk_execute_command,0)!=0){

〃返回值!=0,表示不存在該文件;所以就會(huì)使用老式的方法去執(zhí)行/linuxrc,也就是傳統(tǒng)的initrd格式

ramdiskexecutecommand=NULL;

prepare_namespace();

*Preparethenamespace-decidewhat/wheretomount,loadramdisks,etc.

*

*該函數(shù)完成很多工作,幾乎是Image-initrd流程的全部工作:

*0這個(gè)步驟是有bootloader完成的:bootloader把內(nèi)核以及initrd文件加載到內(nèi)存的特定位置

*1內(nèi)核將initrd的內(nèi)容保存在rootfs下的/initrd.image文件中

*2內(nèi)核將/initrd.image的內(nèi)容讀入/dev/ramO設(shè)備中

*3接著內(nèi)核以可讀寫的方式把/dev/ramO設(shè)備掛載為原始的根文件系統(tǒng)

*4執(zhí)行initrd上的/linuxrc文件

*5等待/linuxrc(linuxrc是通過另外一個(gè)進(jìn)程去執(zhí)行的,所以內(nèi)核使用wait系統(tǒng)調(diào)用來等待linuxrc進(jìn)程)

執(zhí)行完畢

*6將realrootdevicemount到/root目錄,且當(dāng)前目錄就是/root

*7由該函數(shù)倒數(shù)第二行將/root掛載到/。

*

*但是該函數(shù)沒有執(zhí)行/sbin/init程序。該程序的執(zhí)行實(shí)在ini匚post函數(shù)中,該函數(shù)實(shí)現(xiàn)比較巧妙:

*1首先看ramdisk_execute_command是否被設(shè)置,如果被設(shè)置,一般設(shè)置成/init;則/init作為第一個(gè)進(jìn)程

開始執(zhí)行,就是CPIO-initrd

*中執(zhí)行的步驟;

*2在就是看”/sbin/init”這個(gè)可執(zhí)行文件

*3還會(huì)查看其它類似文件,具體請(qǐng)看init_post函數(shù)的實(shí)現(xiàn)。

*

*由此可見,在內(nèi)核3.0中,為支持Image-initrd,就會(huì)多執(zhí)行image-initrd相關(guān)的操作,最后也會(huì)到init_post

函數(shù);

*而CPIO-image會(huì)直接調(diào)用initpost函數(shù);在init_post函數(shù)中根據(jù)ramdisk_execute_command參數(shù)是否

被設(shè)置來決定執(zhí)行:

*/init(包含在CPIO-initrd)還是/sbin/init(包含在realrootdevice)o

*

*

*由此可見,CPIdinitrd在/init就交出了控制權(quán),而Image?initrd在/linuxrc暫時(shí)交出控制權(quán),然后又回收,

最后將控制權(quán)交給了/sbin/inito

*

*/

void_initprepare_namespace(void)

{

intisfloppy;

if(rootdelay){

printk(KERN_INFO"Waiting%dsecbeforemountingrootdevice..Anu,

root_delay);

ssleep(rootdelay);

*waitfbrtheknowndevicestocompletetheirprobing

*

*Note:thisisapotentialsourceoflongbootdelays.

*Forexample,itisnotatypicaltowait5secondshere

*fbrthetouchpadofalaptoptoinitialize.

*/

wait_fbr_device_probe();

〃軟件磁盤陣列相關(guān)的設(shè)備:/dev/mdO

md_run_setup();

if(saved_root_name[0]){

root_device_name=savedrootname;

〃如果rootdevice是mtd或者ubi

if(!strncmp(root_device_name,nmtdn,3)||

nn

!stmcmp(root_device_name,ubi?3)){

mount_block_root(root_device_name,root_mountflags);

gotoout;

}

ROOTDEV=name_to_dev_t(root_device_name);

〃去掉傳給kernel的root參數(shù)的前5個(gè)字符:/dev/

if(stmcmp(root_device_name,H/dev/n,5)==0)

root_device_name+=5;

*加initrd加載到內(nèi)存,具體的工作分為如下幾步:

*0這個(gè)步驟是有bootloader完成的:bootloader把內(nèi)核以及initrd文件加載至lj內(nèi)存的特定位置

*1內(nèi)核將initrd的內(nèi)容保存在rootfs下的/initrd.image文件中

*2內(nèi)核將/initrd.image的內(nèi)容讀入/dev/ramO設(shè)備中

*3接著內(nèi)核以可讀寫的方式把/dev/ramO設(shè)備掛載為原始的根文件系統(tǒng)

*4執(zhí)行initrd上的/linuxrc文件

*5等待/linuxrc(linuxrc是通過另外一個(gè)進(jìn)程去執(zhí)行的,所以內(nèi)核使用wait系統(tǒng)調(diào)用來等待linuxrc

進(jìn)程)執(zhí)行完畢

*6將realrootdevicemount到/root目錄,且當(dāng)前目錄就是/root

*/

if(initrd_load())

gotoout;

/*waitfbranyasynchronousscanningtocomplete*/

if((ROOT_DEV=0)&&root_wait){

printk(KERN_INFOnWaitingfbrrootdevice%s...\nu,

saved_root_name);

while(driver_probe_done()!=0||

(ROOTDEV=name_to_dev_t(saved_root_name))==0)

msleep(100);

async_synchronize_full();

is_floppy=MAJOR(ROOT_DEV)==FLOPPY_MAJOR;

if(is_floppy&&rddoload&&rdloaddisk(O))

ROOTDEV=Root_RAM0;

mount_root();

out:

devtmpfs_mount(ndevn);

〃將realrootdevice掛載到/;原先掛載在/root下。

〃至此,我們已經(jīng)完成image?initrd的掛載、執(zhí)行其中的/linuxrc并等待結(jié)束、通過mount_root將

//realrootdevice掛載到/root目錄、如果可能就將/dev/ram掛載/root/initrd等一系列工作。

〃其實(shí)到此我們已經(jīng)完成image-initrd相關(guān)的所有工作;

〃下面一句就是將/root(realrootdevice)掛載到/目錄,也就完成了image?initrd的中間步驟。

sys_mount(n.';f7n,NULL,MS_MOVE,NULL);

sys_chroot((constchar_user_force*)”.");

接著,我們來詳細(xì)看看initrdjoad函數(shù)的細(xì)節(jié):

int_initinitrdload(void)

(

if(mount_initrd){

〃創(chuàng)建/dev/ram這個(gè)設(shè)備節(jié)點(diǎn)

create_dev(,7dev/ramn,RootRAMO);

/*

*Loadtheinitrddatainto/dev/ramO.Executeitasinitrd

*unless/dev/ramOissupposedtobeouractualrootdevice,

*inthatcasetheramdiskisjustsetuphere,andgets

*mountedinthenormalpath.

*/

〃由上面的內(nèi)核注釋可以比較清楚地看出下面幾句的含義,這樣清晰的注釋在內(nèi)核并不多見

〃首先通過rdloadimage函數(shù)將initrdimage從/initrd.image加載到/dev/ramO

〃接著判斷如果/dev/ramO不是真正的根文件系統(tǒng),執(zhí)行if中的兩個(gè)函數(shù),其實(shí)就是

//image-initrd掛載的其他步驟,在分析handleinitrd的時(shí)候給出細(xì)節(jié)。

if(rd_load_image('7initrd.image")&&ROOTDEV!=RootRAMO){

sys_unlink('7initrd.image");

handle_initrd();

return1;

}

}

sys_unlink(,7initrd.imageH);

return0;

省略一些無關(guān)緊要的代碼。

/*

*正確返回1;

*/

int_initrd_load_image(char*from)

(

intres=0;

intinfd,outfd;

unsignedlongrd_blocks,devblocks;

intnblocks,i,disk;

char*buf=NULL;

unsignedshortrotate=0;

decompress_fndecompressor=NULL;

#if!defined(CONFIG_S390)&&!defined(CONFIG_PPC_ISERIES)

charrotator[4]={T,7,,,-,,,\V);

#endif

//[1]分別打開存放initrdimage的源文件和目的文件

outfd=sys_open((constchar_user_force*)n/dev/ram",ORDWR,0);

if(out_fti<0)

gotoout;

in_fd=sys_open(from,ORDONLY,0);

if(in_fd<0)

gotonoclose_input;

/*[2]

*該函數(shù)只是用來驗(yàn)證initrdimage,該image目前存儲(chǔ)在/initrd.image文件中:

*1returnsthenumberofblockstoreadforanon-compressedimage

*2returns0iftheimageisacompressedimage,

*3returns-1ifanimagewiththerightmagicnumberscouldnotbefound.

*/

nblocks=identify_ramdisk_image(in_fcl,rd_image_start,&decompressor);

if(nblocks<0)

gotodone;

/*[3]

*/initrd.image中存儲(chǔ)的是壓縮格式的initrdimageo

*將in_fd(/initrd.image)中的內(nèi)容解壓到out_fii(/dev/ram)o

*/

if(nblocks=0){

/*

*如果initrdimage是壓縮的,通過crdload函數(shù)將image解壓到outfd

*/

if(crd_load(in_fd,outfd,decompressor)=0)

gotosuccessfulload;

gotodone;

}

//[4]如果initrd是非壓縮的,將其從/initrd.image拷貝到/dev/ram中

/*

*NOTENOTE:nblocksisnotactuallyblocksbut

*thenumberofkibibytesofdatatoloadintoaramdisk.

*SoanyraindiskblocksizethatisamultipleofIKiBshould

*workwhentheappropriateramdisk_blocksizeisspecified

*onthecommandline.

*

*Thedefaultramdisk_blocksizeisIKiBanditisgenerally

*sillytouseanythingelse,somakesuretouseIKiB

*blocksizewhilegeneratingext2fsramdisk-images.

*

*該函數(shù)返回out_fd中的塊的個(gè)數(shù),即/dev/ram的塊的個(gè)數(shù)。

*/

if(sys_ioctl(out_fti,BLKGETSIZE,(unsignedlong)&rd_blocks)<0)

rd_blocks=0;

else

rdblocks?=1;

/*

*如果將in_fd(/initrd.image)中的塊的數(shù)目大于out_fd(/dev/ram)中的一半,出錯(cuò)。

*如果塊的個(gè)數(shù)大于塊的大小的一半時(shí),出錯(cuò)。

*/

if(nblocks>rdblocks){

printk(nRAMDISK:imagetoobig!(%dKiB/%IdKiB)\nu,

nblocks,rdblocks);

gotodone;

*OK,timetocopyinthedata

*/

if(sys_ioctl(in_fa,BLKGETSIZE,(unsignedlong)&devblocks)<0)

devblocks=0;

else

devblocks?=1;

if(strcmp(from,n/initrd.imageu)==0)

devblocks=nblocks;

if(devblocks==0){

printk(KERN_ERR"RAMDISK:couldnotdeterminedevicesize\nn);

gotodone;

)

buf=kmalloc(BLOCK_SIZE,GFP_KERNEL);

if(!buf){

printk(KERN_ERR"RAMDISK:couldnotallocatebuffer\nM);

gotodone;

}

printk(KERN_NOTICE"RAMDISK:Loading%dKiB[%lddisk%s]intoramdisk...n,

nblocks,((nblocks-1)/devblocks)+1,nblocks>devblocks?:"”);

for(i=0,disk=1;i<nblocks;i++){

if(i&&(i%devblocks=0)){

printk(udonedisk#%d.\n",disk-H-);

rotate=0;

if(sys_close(in_fcl)){

printk(uErrorclosingthedisk.'n”);

gotonoclose_input;

)

changefloppy(Hdisk#%d",disk);

infd=sys_open(from,ORDONLY,0);

if(in_fd<0){

printk(HErroropeningdisk.\nn);

gotonoclose_input;

)

printk(HLoadingdisk#%d...",disk);

}

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論