![Linu內(nèi)核解析資料_第1頁](http://file4.renrendoc.com/view14/M05/34/1B/wKhkGWd7wGeAIoJ7AAGL4iXOgj8625.jpg)
![Linu內(nèi)核解析資料_第2頁](http://file4.renrendoc.com/view14/M05/34/1B/wKhkGWd7wGeAIoJ7AAGL4iXOgj86252.jpg)
![Linu內(nèi)核解析資料_第3頁](http://file4.renrendoc.com/view14/M05/34/1B/wKhkGWd7wGeAIoJ7AAGL4iXOgj86253.jpg)
![Linu內(nèi)核解析資料_第4頁](http://file4.renrendoc.com/view14/M05/34/1B/wKhkGWd7wGeAIoJ7AAGL4iXOgj86254.jpg)
![Linu內(nèi)核解析資料_第5頁](http://file4.renrendoc.com/view14/M05/34/1B/wKhkGWd7wGeAIoJ7AAGL4iXOgj86255.jpg)
版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年家庭智能設(shè)備借款協(xié)議書
- 2025年城市軌道交通施工安全合同標(biāo)準(zhǔn)文本
- 2025年甲乙丙三方合作借款策劃協(xié)議書
- 2025年代理服務(wù)協(xié)議書
- 2025年挖掘機(jī)施工服務(wù)合同協(xié)議
- 2025年公司股權(quán)激勵(lì)方案協(xié)議
- 2025年原廠租賃設(shè)備到期收購(gòu)合同范本
- 2025年公司定期存款質(zhì)權(quán)借款業(yè)務(wù)合同模板
- 2025年度名譽(yù)顧問服務(wù)協(xié)議
- 2025年企業(yè)資本運(yùn)作與合作投資框架協(xié)議
- 生物(A版)-安徽省合肥一中(省十聯(lián)考)2024-2025學(xué)年度高二年級(jí)上學(xué)期期末測(cè)試試題和答案
- 蘇教版四年級(jí)數(shù)學(xué)下冊(cè)第三單元第二課時(shí)《常見的數(shù)量關(guān)系》課件
- 2025年中考物理總復(fù)習(xí)《壓強(qiáng)》專項(xiàng)測(cè)試卷含答案
- 《智能傳感器技術(shù)》課件
- SaaS服務(wù)具體應(yīng)用合同范本2024版版
- 山東省濰坊市2024-2025學(xué)年高三上學(xué)期1月期末 政治試題(含答案)
- 2025-2030年中國(guó)旅居康養(yǎng)行業(yè)全國(guó)市場(chǎng)開拓戰(zhàn)略制定與實(shí)施研究報(bào)告
- 知識(shí)產(chǎn)權(quán)培訓(xùn)內(nèi)容課件
- 2025年幼兒園年度工作總結(jié)及工作計(jì)劃
- 殘疾人掛靠合作合同協(xié)議書范本
- 《物料擺放規(guī)范》課件
評(píng)論
0/150
提交評(píng)論