小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

linux2.6內(nèi)核initrd機(jī)制解析

 joy_chen 2013-01-16
http://blog.csdn.net/new_abc/article/details/7693655

http://womendu./blog/1069948


很久之前就分析過這部分內(nèi)容,但是那個(gè)時(shí)候不夠深入,姑且知道這么個(gè)東西存在,到底怎么用,來龍去脈咋回事就不知道了。前段時(shí)間工作上遇到了一個(gè)initrd的問題,沒辦法只能再去研究研究,還好,有點(diǎn)眉目,索性整理了一下。

網(wǎng)絡(luò)上流傳著很多關(guān)于ramdisk、initrd的各種版本的分析,我的這篇源于對他們的理解,非常感謝那些前輩的無私奉獻(xiàn),要不然我們這些晚輩學(xué)起東西來該是多么艱難呀。在這里需要特別聲明的是如果文中有引用了您的思想而沒有給出參考文獻(xiàn),請您原諒我的疏忽。晚輩就是需要站在像您這種巨人的肩上,技術(shù)才會發(fā)展,社會才會進(jìn)步。
另外,由于本人水平有限,文中難免有誤解及不全面之處,煩請指正,謝謝!
歡迎轉(zhuǎn)載,但請保留原文該有的信息。

聯(lián)系方式:李枝果/lizgo lizhiguo0532@163.com 2010/10/16

目錄
第一部分:ramfs、tmpfs、rootfs、ramdisk
一、 什么是ramfs
二、 什么是tmpfs
三、 什么是rootfs
四、 什么是ramdisk
第二部分:initrd、initramfs
一、 initrd出現(xiàn)的背景
二、 initrd的種類和制作
第三部分:kernel初始化initrd代碼分析
一、 bootloader傳遞給內(nèi)核的關(guān)于initrd的參數(shù)
二、 動(dòng)態(tài)內(nèi)存分配器slab介紹
三、 rootfs初始化
四、 內(nèi)核初始化階段對initrd的處理
五、 老式塊設(shè)備的initrd的處理函數(shù)prepare_namespace()分析
附文
參考網(wǎng)址


第一部分:ramfs、tmpfs、rootfs、ramdisk
一、 什么是ramfs?
1. linux緩存機(jī)制
VFS(虛擬文件系統(tǒng))層屏蔽了各種真實(shí)文件系統(tǒng)的特性,提供給linux上層統(tǒng)一的接口。
通常,linux對所有文件的讀寫都會在內(nèi)存在做高速緩存,當(dāng)系統(tǒng)再次使用這些文件時(shí),可以直接從內(nèi)存中讀取,以提高系統(tǒng)的I/O性能。當(dāng)高速緩存中的文件被修改或者有數(shù)據(jù)寫入高速緩存時(shí),系統(tǒng)會在適當(dāng)?shù)臅r(shí)候?qū)⑦@些高速緩存中的數(shù)據(jù)回寫到對應(yīng)的文件系統(tǒng)設(shè)備(如磁盤、flash等)中去,那么這之后這些高速緩存的狀態(tài)就被標(biāo)識為clean(可用),這樣就相當(dāng)于告訴了系統(tǒng):這些高速緩存中的數(shù)據(jù)文件系統(tǒng)設(shè)備上有備份,你可以拿去另作他用。但其中的數(shù)據(jù)會保持到VMS(Virtual Memory System)將這些高速緩存回收重新分配。
類似于頁緩存機(jī)制,目錄緩存機(jī)制也極大地加快了對目錄的訪問。
2. ramfs
ramfs是一種非常簡單的文件系統(tǒng),它直接利用linux內(nèi)核已有的高速緩存機(jī)制(所以其實(shí)現(xiàn)代碼很小,也由于這個(gè)原因,ramfs特性不能通過內(nèi)核配置參數(shù)屏蔽,它是內(nèi)核的天然屬性),使用系統(tǒng)的物理內(nèi)存,做成一個(gè)大小可以動(dòng)態(tài)變化的的基于內(nèi)存的文件系統(tǒng)。
ramfs工作于虛擬文件系統(tǒng)層(VFS)層,不能被格式化,可以創(chuàng)建多個(gè),默認(rèn)情況下,ramfs最多能用到內(nèi)存的一半,必要時(shí)也可以使用-o maxsize = 10000(單位是KB)來更改使用的最大內(nèi)存量。
ramfs沒有對應(yīng)的文件系統(tǒng)設(shè)備,文件被寫入ramfs和其他文件系統(tǒng)一樣都正常分配頁緩存和目錄緩存,但是卻不可能想其他有存儲設(shè)備的文件系統(tǒng)一樣將高速緩存中的文件回寫到存儲設(shè)備。這就意味著這些為ramfs中的文件和目錄分配的高速頁或者目錄緩存都不可能會被標(biāo)記為clean(可用)狀態(tài),所以系統(tǒng)就永遠(yuǎn)不會釋放ramfs所占用的內(nèi)存。正因?yàn)榭梢栽趓amfs下面可以一直往里寫數(shù)據(jù),直到寫滿為止,所以這種操作只有root(or trusted user)用戶才可以進(jìn)行ramfs寫操作。
為了解決ramfs的缺點(diǎn)(沒有回寫設(shè)備)導(dǎo)致的種種問題,所以衍生出了tmpfs文件系統(tǒng)。

二、 什么是tmpfs?
tmpfs是ramfs的衍生物,在ramfs的基礎(chǔ)上增加了容量大小的限制和允許向交換
空間(swap) 寫入數(shù)據(jù)。由于增加了這兩個(gè)特性,所以普通用戶也可以使用tmpfs。
tmpfs是一種虛擬內(nèi)存文件系統(tǒng),它不同于傳統(tǒng)的用塊設(shè)備形式來實(shí)現(xiàn)的ramdisk,也不同于針對物理內(nèi)存的ramfs。tmpfs既可以使用物理內(nèi)存,也可以使用交換分區(qū)。在linux內(nèi)核中,虛擬內(nèi)存資源由物理內(nèi)存和交換分區(qū)組成,這些資源由內(nèi)核中的虛擬內(nèi)存子系統(tǒng)來負(fù)責(zé)管理。tmpfs就是和虛擬內(nèi)存子系統(tǒng)打交道的,它向虛擬內(nèi)存子系統(tǒng)請求頁來存儲文件,同linux的其他請求頁的部分一樣,不知道分配給自己的頁是在內(nèi)存中還是在交換分區(qū)中。也就是說tmpfs使用的是虛擬內(nèi)存,而ramfs使用物理內(nèi)存。另外tmpfs和ramfs一樣,不可以被格式化,同時(shí)大小也是不固定的,可以使用-o size =32m或者(1g)來修改。
另外,tmpfs可以將當(dāng)前不需要使用的頁寫入到交換空間。同時(shí)由于其使用的虛擬內(nèi)存,所以tmpfs一旦被卸載,其中的數(shù)據(jù)都會丟失。
如果需要使用tmpfs,在內(nèi)核編譯的時(shí)候得選擇上:
Virtual memory filesystem support
ramfs只會在物理內(nèi)存中被創(chuàng)建,而tmpfs可能在物理內(nèi)存中創(chuàng)建,也可能在交換 分區(qū)中創(chuàng)建。對于想利用內(nèi)存的高速IO來提高效能的應(yīng)用,最好是使用ramfs。對于只是想存放臨時(shí)緩存的應(yīng)用,最好使用tmpfs,以提高內(nèi)存的使用率。

三、 什么是rootfs?
rootfs是一個(gè)特定的ramfs(或tmpfs,如果tmpfs被啟用)的實(shí)例,它始終存在于linux2.6的系統(tǒng)中。rootfs不能被卸載(與其添加特殊代碼用來維護(hù)空的鏈表,不如把rootfs節(jié)點(diǎn)始終加入,因此便于kernel維護(hù)。rootfs是ramfs的一個(gè)空實(shí)例,占用空間極小)。大部分其他的文件系統(tǒng)安裝于rootfs之上,然后忽略它。它是內(nèi)核啟動(dòng)初始化根文件系統(tǒng)。

四、 什么是ramdisk?
linux2.6版本之后都不在使用ramdisk了,2.4中還在使用。ramdisk是一種將內(nèi)存
中的的一塊區(qū)域作為物理磁盤來使用的一種技術(shù),也可以說,ramdisk是在一塊內(nèi)存區(qū) 域中創(chuàng)建的塊設(shè)備,用于存放文件系統(tǒng)。對于用戶來說,可以把ramdisk與通常的硬盤分區(qū)同等對待來使用。ramdisk不適合作為長期保存文件的介質(zhì),掉電后ramdisk的內(nèi)容會消失。
為了能夠使用ramdisk 你的內(nèi)核必須要支持ramdisk,即:在編譯內(nèi)核時(shí),要選中RAM disk support這一選項(xiàng),會在配置文件中定義CONFIG_BLK_DEV_RAM。同時(shí)為了讓內(nèi)核有能力在內(nèi)核加載階段就能裝入ramdisk,并運(yùn)行其中的內(nèi)容,要選中initial RAM disk(initrd) support 選項(xiàng),會在配置文件中定義CONFIG_BLK_DEV_INITRD。
ramdisk的大小是固定的,安裝在其上的文件系統(tǒng)大小也是固定的。ramdisk在使用的時(shí)候,這個(gè)假的塊設(shè)備和高速緩存(頁緩存和目錄緩存)之間有數(shù)據(jù)的拷貝,而且它還需要文件系統(tǒng)的驅(qū)動(dòng)來格式化和解釋這些數(shù)據(jù)。所以,使用ramdisk不僅浪費(fèi)了內(nèi)存,還加重了cpu的負(fù)擔(dān),同時(shí)也無污染了cache,而且其所有的文件和目錄都要通過頁和目錄緩存進(jìn)行訪問,這些工作ramfs都要執(zhí)行的,那么ramdisk就可以完全不需要。這是廢棄ramdisk的理由之一。
另一個(gè)廢棄它的理由就是,回環(huán)設(shè)備的引進(jìn),而回環(huán)設(shè)備提供了一個(gè)更靈活和方便的方式(從文件而不是從大塊的內(nèi)存)來創(chuàng)建一個(gè)合成塊設(shè)備。

第二部分:initrd、initramfs
一、 initrd出現(xiàn)的背景
在早期的linux系統(tǒng)中,一般只有硬盤或者軟盤被用來作為linux根文件系統(tǒng)的存儲設(shè)備,因此也就很容易把這些設(shè)備的驅(qū)動(dòng)程序集成到內(nèi)核中。但是現(xiàn)在的嵌入式系統(tǒng)中可能將根文件系統(tǒng)保存到各種存儲設(shè)備上,包括scsi、sata,u-disk等等。因此把這些設(shè)備的驅(qū)動(dòng)代碼全部編譯到內(nèi)核中顯然就不是很方便。
在內(nèi)核模塊自動(dòng)加載機(jī)制udev中,我們看到利用udevd可以實(shí)現(xiàn)內(nèi)核模塊的自動(dòng)加載,因此我們希望如果存儲根文件系統(tǒng)的存儲設(shè)備的驅(qū)動(dòng)程序也能夠?qū)崿F(xiàn)自動(dòng)加載,那就好了。但是這里有一個(gè)矛盾,udevd是一個(gè)可執(zhí)行文件,在根文件系統(tǒng)被掛載前,是不可能執(zhí)行udevd的,但是如果udevd沒有啟動(dòng),那就無法自動(dòng)加載存儲根文件系統(tǒng)設(shè)備的驅(qū)動(dòng)程序,同時(shí)也無法在/dev目錄下建立相應(yīng)的設(shè)備節(jié)點(diǎn)。
為了解決這一矛盾,于是出現(xiàn)了基于ramdisk的initrd( bootloader initialized RAM disk )。Initrd是一個(gè)被壓縮過的小型根目錄,這個(gè)目錄中包含了啟動(dòng)階段中必須的驅(qū)動(dòng)模塊,可執(zhí)行文件和啟動(dòng)腳本,也包括上面提到的udevd(實(shí)現(xiàn)udev機(jī)制的demon)。當(dāng)系統(tǒng)啟動(dòng)的時(shí)候,bootloader會把initrd文件讀到內(nèi)存中,然后把initrd文件在內(nèi)存中的起始地址和大小傳遞給內(nèi)核。內(nèi)核在啟動(dòng)初始化過程中會解壓縮initrd文件,然后將解壓后的initrd掛載為根目錄,然后執(zhí)行根目錄中的/init腳本(cpio格式的initrd為/init,而image格式的initrd<也稱老式塊設(shè)備的initrd或傳統(tǒng)的文件鏡像格式的initrd>為/initrc),您就可以在這個(gè)腳本中運(yùn)行initrd文件系統(tǒng)中的udevd,讓它來自動(dòng)加載realfs(真實(shí)文件系統(tǒng))存放設(shè)備的驅(qū)動(dòng)程序以及在/dev目錄下建立必要的設(shè)備節(jié)點(diǎn)。在udevd自動(dòng)加載磁盤驅(qū)動(dòng)程序之后,就可以mount真正的根目錄,并切換到這個(gè)根目錄中來。
這里只是個(gè)簡單的描述,后面慢慢分析吧。

二、 initrd的種類和制作
initrd總的來說目前有兩種格式:image格式和cpio格式。image格式也叫文件系統(tǒng)鏡像文件(老式塊設(shè)備的initrd文件),主要在linux 2.4內(nèi)核中使用流行;在linux 2.5內(nèi)核開始引入initramfs技術(shù),initramfs實(shí)際上已經(jīng)克服了imgae-initrd的缺點(diǎn),本質(zhì)上也是cpio格式的initrd,只不過是和內(nèi)核編譯到了一個(gè)image文件,放在了.init.ramfs段內(nèi);到linux2.6的內(nèi)核支持兩種格式的initrd,即image-initrd和cpio-initrd,此時(shí)的cpio-initrd文件已不再編譯進(jìn)內(nèi)核而單獨(dú)成一文件,使用cpio工具生成。后邊詳述。

在介紹initrd的制作之前,先了解下面的幾個(gè)命令作用:
dd:用指定大小的塊拷貝一個(gè)文件,并在拷貝的同時(shí)進(jìn)行指定的轉(zhuǎn)換。
1. if=文件名:輸入文件名,缺省為標(biāo)準(zhǔn)輸入。即指定源文件。< if=input file >
注意/dev/zero設(shè)備,這個(gè)設(shè)備是可以源源不斷地提供0的設(shè)備,用來初始化
2. of=文件名:輸出文件名,缺省為標(biāo)準(zhǔn)輸出。即指定目的文件。< of=output file >
3.bs=bytes:同時(shí)設(shè)置讀入/輸出的塊大小為bytes個(gè)字節(jié)。
4. count=blocks:僅拷貝blocks個(gè)塊,塊大小等于ibs指定的字節(jié)數(shù)。
還有其他更詳細(xì)的參數(shù)用法參考:dd命令-詳解
http://blog.csdn.net/liumang_D/archive/2009/02/17/3899462.aspx

mkfs.ext2:等同于mke2fs,建立ext2文件系統(tǒng)。
mke2fs [-cFMqrSvV][-b <區(qū)塊大小>][-f <不連續(xù)區(qū)段大小>][-i <字節(jié)>][-N ][-l <文件>][-L <標(biāo)簽>][-m <百分比值>][-R=<區(qū)塊數(shù)>][ 設(shè)備名稱][區(qū)塊數(shù)]
1. -F 不管指定的設(shè)備為何,強(qiáng)制執(zhí)行mke2fs。
2. -m<百分比值> 指定給管理員保留區(qū)塊的比例,預(yù)設(shè)為5%。
3. 其余參數(shù)默認(rèn)即可,參考: mkfs.ext2 命令-詳解(網(wǎng)絡(luò)搜索)
mount:掛在命令.
mount [-t vfstype] [-o options] device dir
1.-t vfstype 指定文件系統(tǒng)的類型,通常不必指定。mount 會自動(dòng)選擇正確的類型。
2.-o options 主要用來描述設(shè)備或檔案的掛接方式。常用的參數(shù)有:
 loop:用來把一個(gè)文件當(dāng)成硬盤分區(qū)掛接上系統(tǒng)
 ro:采用只讀方式掛接設(shè)備
 rw:采用讀寫方式掛接設(shè)備
 iocharset:指定訪問文件系統(tǒng)所用字符集
3.詳細(xì)參數(shù)參考:mount命令詳解(網(wǎng)絡(luò)搜索)

1)、image-initrd制作
我們可以通過下面的方法來制作一個(gè)老式基于塊設(shè)備的image-initrd文件:
# dd if=/dev/zero of=initrd.img bs=4k count=1024
# mkfs.ext2 -F –m 0 initrd.img
# sudo mkdir /mnt/ramdisk
# mount -o loop initrd.img /mnt/ramdisk
# cp -r /opt/filesystem /mnt/ramdisk
# umount /mnt
# gzip -9 initrd.img
通過上面的命令,我們制作了一個(gè)4M的initrd,其中/opt/filesystem就是我們用busybox制作的一個(gè)根目錄。最后我們得到一個(gè)名為initrd.img.gz的壓縮比最大的壓縮文件。更加詳細(xì)的過程,參考:ramdisk制作
http://blog.csdn.net/epicyong333/archive/2008/12/24/3590619.aspx
http://blog.csdn.net/vcvbve/archive/2010/02/27/5329384.aspx

利用image-initrd可以使內(nèi)核在啟動(dòng)階段可以順利地完成各種存儲介質(zhì)的驅(qū)動(dòng)的加載和realfs文件系統(tǒng)的掛載,然而image-initrd存在以下缺點(diǎn):
1.image-initrd大小是固定的,例如上面的壓縮之前的initrd大小是4M(4k*1024),假設(shè)您的根目錄(上例中的/opt/filesystem)總大小僅僅是1M,它仍然要占用4M的空間。如果您在dd階段指定大小為1M,后來發(fā)現(xiàn)不夠用的時(shí)候,必須按照上面的步驟重新來一次。
2.image-initrd是一個(gè)虛擬的塊設(shè)備,您可是使用fdisk對這個(gè)虛擬塊設(shè)備進(jìn)行分區(qū)。在內(nèi)核中,對塊設(shè)備的讀寫還要經(jīng)過緩沖區(qū)管理模塊,也就是說,當(dāng)內(nèi)核讀取initrd中的文件內(nèi)容時(shí),緩沖區(qū)管理層會認(rèn)為下層的塊設(shè)備速度比較慢,因此會啟用預(yù)讀和緩存功能。這樣initrd本身就在內(nèi)存中,同時(shí)塊設(shè)備緩沖區(qū)管理層還會保存一部分內(nèi)容。

2)、initramfs
為了避免上述缺點(diǎn),在linux2.5中出現(xiàn)了initramfs,它的作用和initrd類似,只是和內(nèi)核編譯成一個(gè)文件(該initramfs是經(jīng)過gzip壓縮后的cpio格式的數(shù)據(jù)文件),該cpio格式的文件被鏈接進(jìn)了內(nèi)核中特殊的數(shù)據(jù)段.init.ramfs上,其中全局變量__initramfs_start和__initramfs_end分別指向這個(gè)數(shù)據(jù)段的起始地址和結(jié)束地址。內(nèi)核啟動(dòng)時(shí)會對.init.ramfs段中的數(shù)據(jù)進(jìn)行解壓,然后使用它作為臨時(shí)的根文件系統(tǒng)。
要制作這樣的內(nèi)核,我們只需要在make menuconfig中配置以下選項(xiàng)就可以了: 
General setup ---> 
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support 
(/opt/filesystem) Initramfs source file(s) 
其中/opt/filesystem就是我們的小型根目錄,這里可以使一個(gè)現(xiàn)成的gzip壓縮的cpio文件,也可以使一個(gè)目錄,更可以是txt格式的配置文件,如下面的實(shí)例:
dir /dev 755 0 0
nod /dev/console 644 0 0 c 5 1
nod /dev/loop0 644 0 0 b 7 0
dir /bin 755 1000 1000
slink /bin/sh busybox 777 0 0
file /bin/busybox initramfs/busybox 755 0 0
dir /proc 755 0 0
dir /sys 755 0 0
dir /mnt 755 0 0
file /init initramfs/init.sh 755 0 0
如果指定的是一個(gè)目錄而不是一個(gè)像這樣的配置文件,內(nèi)核在編譯的時(shí)候會從指定的目錄創(chuàng)建一個(gè)配置文件(usr/Makefile調(diào)用scripts/gen_initramfs_list.sh來生成),作為usr/gen_init_cpio.c文件的輸入,最后生成一個(gè)usr/initramfs_data.cpio.gz文件,通過usr/initramfs_data.S包含到.init.ramfs段中去,最后生成zImage。

3)、cpio-initrd
這里所說的initrd格式和編譯進(jìn)內(nèi)核的initramfs格式是一樣的,都是cpio,也
被稱為外部initramfs,它是獨(dú)立存在的,需要bootloader將其加載進(jìn)內(nèi)存特定地址,然后將地址和大小傳遞給內(nèi)核,在內(nèi)核的初始化階段來進(jìn)行相應(yīng)的處理。
這種initrd可以使用cpio命令來實(shí)現(xiàn),如下:
# sudo find /opt/filesystem/ -depth | cpio -c -o > initrd.img 
# gzip -9 initrd.img
這樣得到的initrd就是cpio格式的,而且這個(gè)文件的大小是可變的,意思就是根據(jù)
你的filesystem的大小而變化,不會像前面的image格式的initrd那樣大小固定了。當(dāng)然我覺得前面的initramfs的大小也是可變的了。

關(guān)鍵詞:
ramfs、tmpfs、rootfs、ramdisk
image-initrd、initramfs、cpio-initrd

第三部分:kernel初始化initrd代碼分析
arm平臺、linux2.6.29
一、 bootloader傳遞給內(nèi)核的關(guān)于initrd的參數(shù)
root=
該參數(shù)告訴內(nèi)核以那個(gè)設(shè)備作為根文件系統(tǒng),通常由如下形式:
root=/dev/ramx
root=/dev/mtdblockx rw rootfstype = jffs2
root=31:0x rootfstype = jffs2
root=/dev/nfs nfsroot=serverip:nfs_dir
第一種在使用ramdisk和image-initrd的時(shí)候是必須的,initramfs和cpio-initdrd因?yàn)槭褂玫氖窍到y(tǒng)內(nèi)一直存在的rootfs,所以該參數(shù)可以省略。
第三、四種基本通用,只是,第四種使用的設(shè)備號和分區(qū)號來指定,比如root=/dev/mtdblock4 等價(jià)于 root=31:04,意思就是使用nand設(shè)備上的第四分區(qū)來作為根文件系統(tǒng), 同時(shí)用rootfstype指定文件系統(tǒng)類型。
最后一種就是掛在nfs文件系統(tǒng)了,除了要指定root類型之外,還需要指定服務(wù)器上的某個(gè)目錄來作為掛載的根目錄。

initrd=
應(yīng)遵循這種格式:initrd = addr, size ,其中addr表示initrd在內(nèi)存中的位置,size表示initrd的大小。如initrd = 0xa060000,0x2c6b
另外一種傳遞該參數(shù)的方法就是:使用tag參數(shù)來傳遞,uboot中使用的是函數(shù)setup_initrd_tag()函數(shù)來生成對應(yīng)的tag參數(shù)。
需要注意的有兩點(diǎn):
1. 內(nèi)核初始化先解析tag中的initrd地址和大小參數(shù),然后再去會去執(zhí)行cmdline解析initrd=參數(shù),如果這兩個(gè)地方都指定了話,那么最終取決于cmdline中initrd=所指定的參數(shù),因?yàn)檫@兩種方法傳遞的結(jié)果都是被內(nèi)核初始化階段的全局變量phys_initrd_start和phys_initrd_size中(這是物理地址),然后再轉(zhuǎn)換成虛擬地址存儲在全局變量initrd_start和initrd_size中,供后續(xù)使用。
2. bootloader傳遞的大小應(yīng)該是initrd的實(shí)際大小,不能多傳更不能少傳,否則,gunzip解壓縮的時(shí)候會出錯(cuò)。

init=
init指定的是內(nèi)核啟起來后,進(jìn)入系統(tǒng)中運(yùn)行的第一個(gè)腳本,一般init=/linuxrc(image-initrd使用)和init=/init(initramfs和cpio-initrd使用)。
ramdisk使用rdinit=/xxx來指定。

小結(jié):( 以下結(jié)論均在initrd的地址和大小均通過tag參數(shù)傳遞)
通過后面的代碼分析,可以做如下總結(jié):
1. 使用initramfs和cpio-initrd
不需要root=xxx參數(shù),而init=/init是必須的
2. 使用image-initrd(記得打開內(nèi)核對ramdisk和initrd的支持)
init=/initrc,下面是傳遞不同root=xxx的情況
a. root=/dev/ramx,將image-initrd作為真實(shí)的文件系統(tǒng)在使用
b. root=/dev/mtdblockx rw rootfstype = jffs2,這樣image-initrd只是作為一個(gè)中間過渡的文件系統(tǒng),最終還是需要依靠內(nèi)核1號線程將真實(shí)的文件系統(tǒng)掛上來。
(b中的root= 只是舉個(gè)例子,真實(shí)系統(tǒng)放在什么設(shè)備上沒關(guān)系,主要的是需要在image-initrd文件系統(tǒng)中將真是文件系統(tǒng)存在的設(shè)備的驅(qū)動(dòng)程序加載上就好了)

二、 動(dòng)態(tài)內(nèi)存分配器slab介紹
linux的每種動(dòng)態(tài)內(nèi)存管理都使用一種基于堆的分配策略。
原始的分配策略是這樣的,在這種方法中,大塊內(nèi)存(稱為 堆)用來為用戶定義的目的提 供內(nèi)存。當(dāng)用戶需要一塊內(nèi)存時(shí),就請求給自己分配一定大小的內(nèi)存。堆管理器會查看可用內(nèi)存的情況(使用特定算法)并返回一塊內(nèi)存。搜索過程中使用的一些算 法有 first-fit(在堆中搜索到的第一個(gè)滿足請求的內(nèi)存塊 )和 best-fit(使用堆中滿足請求的最合適的內(nèi)存塊)。當(dāng)用戶使用完內(nèi)存后,就將內(nèi)存返回給堆。這種基于堆的分配策略的根本問題是碎片(fragmentation)。當(dāng)內(nèi)存塊被分配后,它們會以不同的順序在不同的時(shí)間返回。這樣會在堆中留下一些洞,需要花一些時(shí)間才能有效地管理空閑內(nèi)存。這種算法通常具有較高的內(nèi)存使用效率(分配需要的內(nèi)存),但是卻需要花費(fèi)更多時(shí)間來對堆進(jìn)行管理。
大約在2.0內(nèi)核之前,出現(xiàn)了另外一種管理機(jī)制稱為 buddy memory allocation(伙伴內(nèi)存分配系統(tǒng)),是一種更快的內(nèi)存分配技術(shù),它將內(nèi)存 劃分為 2 的冪次方個(gè)分區(qū),并使用 best-fit 方法來分配內(nèi)存請求。當(dāng)用戶釋放內(nèi)存時(shí),就會檢查 buddy 塊,查看其相鄰的內(nèi)存塊是否也已經(jīng)被釋放。如果是的話,將合并內(nèi)存塊以最小化內(nèi)存碎片。這個(gè)算法的時(shí)間效率更高,但是由于使用 best-fit 方法的緣故,會產(chǎn)生內(nèi)存浪費(fèi)。
后來在2.2內(nèi)核之后,引入了一種全新的動(dòng)態(tài)內(nèi)存分配器slab分配器,slab分配器的概念首先在Sun Microsystem 的SunOs 5.4操作系統(tǒng)中得以實(shí)現(xiàn),圍繞對象緩存進(jìn)行的。slab層會把不同的對象劃分為所謂的高速緩存(cache)組,其中每個(gè)高速緩存都存放不同類型的對象。每種對象類型對應(yīng)一個(gè)高速緩存。例如,一個(gè)高速緩存用于存放進(jìn)程描述符(task_struct),而另一個(gè)高速緩存存放索引節(jié)點(diǎn)對象(struct inond)。值得一提的是,kmalloc接口建立在slab層之上,使用了一組通用高速緩存。
下圖給出了slab 結(jié)構(gòu)的高層組織結(jié)構(gòu)。在最高層是cache_chain,這是一 個(gè) slab 緩存的鏈接列表。這對于 best-fit 算法非常有用,可以用來查找最適合所需要的分配大小的緩存(遍歷列表)。cache_chain 的每個(gè)元素都是一個(gè) kmem_cache 結(jié)構(gòu)的引用(稱為一個(gè) cache)。它定義了一個(gè)要管理的給定大小的對象池。

圖貼不上,《linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)》書上有

這些高速緩存又劃分為slab,slab又由一個(gè)或多個(gè)物理上連續(xù)的頁組成,每一個(gè)物理頁被劃分為特定的對象。一般情況下,slab也僅僅由一頁組成。
slab分配器的接口:
創(chuàng)建一個(gè)新的高速緩存,它通常在內(nèi)核初始化時(shí)或者首次加載內(nèi)核模塊時(shí)執(zhí)行。
kmem_cache_t *kmem_cache_creat( const char *name, size_t size, 
size_t align,unsigned long flags;
  void (*ctor)(void*, struct kmem_cache *, unsigned long),
  void (*dtor)(void*, struct kmem_cache *, unsigned long));

銷毀一個(gè)高速緩存
int kmem_cache_destroy(kmem_cache_t *cachep);

創(chuàng)建了高速緩存之后,就可以通過下面的函數(shù)從中獲取對象
void *kmem_cache_alloc(kmem_cache_t *cachep,int flags);
如果高速緩存中所有的slab中都沒有空閑的對象,那么slab層必須通過kmem_getpages()獲取新的頁,flags值傳遞給__get_free_pages()。

下面的函數(shù)是釋放對象,將其返還給原先的slab
void kmem_cache_free(kmem_cache_t *cachep,void *objp);

更加詳細(xì)的參看:《linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)》 robrt love著
另外值得提到的是在linux2.6.22之后,引進(jìn)了slub分配器,該分配器保留了slab的基本思想:每個(gè)緩沖區(qū)由多個(gè)小的slab組成,每個(gè)slab包含固定數(shù)目的對象。slub分配器簡化了kmem_cache,slab等相關(guān)的管理數(shù)據(jù)結(jié)構(gòu),摒棄了slab分配器中眾多的隊(duì)列概念,并針對多處理器、NUMA系統(tǒng)進(jìn)行優(yōu)化,從而提高了性能和可擴(kuò)展性并降低了內(nèi)存的浪費(fèi)。為了保證內(nèi)核其它模塊能夠無縫遷移到slub分配器,slub還保留了原有slab分配器所有的接口 API 函數(shù)。
http://qgjie456.blog.163.com/blog/static/3545136720090622056716/
三、 rootfs初始化
該部分代碼主要在init/main.c中的start_kernel()函數(shù)中的vfs_caches_init_early()和vfs_caches_init(num_physpages)來實(shí)現(xiàn),其中num_physpages全局變量在mem_init()函數(shù)中初始化,代表物理內(nèi)存的總頁數(shù),
詳細(xì)的代碼分析見:rootfs_initialize.c
vfs_caches_init_early():
struct hlist_head {
struct hlist_node *first;
};

struct hlist_node {
struct hlist_node *next, **pprev;
};
總結(jié):
inode_hashtable就是一數(shù)組,每個(gè)數(shù)組元素都是一個(gè)struct hlist_head結(jié)構(gòu)體,該結(jié)構(gòu)體實(shí)際上就只有一個(gè)指向struct hlist_node對象的指針first,所有指針在arm體系上都是4字節(jié),所以這個(gè)函數(shù)實(shí)際上只是為數(shù)組inode_hashtable分配了一段內(nèi)存空間,該數(shù)組是實(shí)際上就是一個(gè)指針數(shù)組,每個(gè)元素都是指向struct hlist_node對象的指針。然后最后將這些指針全部置成NULL。至于后續(xù)怎么使用還沒涉及到。
同樣類似地,對于Dentry cache哈希表(dentry_hashtable)也是一樣的,只是創(chuàng)建了一個(gè)指針數(shù)組而已。
http://qgjie456.blog.163.com/blog/static/3545136720081126102615685/

四、 內(nèi)核初始化階段對initrd的處理
start_kernel()函數(shù)最后會調(diào)用rest_init(),在該函數(shù)中會創(chuàng)建兩個(gè)內(nèi)核線程kernel_init和kthreadd(2.6.14內(nèi)核中只創(chuàng)建了一個(gè)內(nèi)核線程init,但是所做工作基本一樣),內(nèi)核線程創(chuàng)建后,原來的系統(tǒng)0號進(jìn)程進(jìn)入idle狀態(tài)。
對initrd的處理函數(shù)主要有兩個(gè):populate_rootfs()和prepare_namespace(),針對不同的格式,處理情況各不相同。
這里注意一點(diǎn)的是:populate_rootfs()函數(shù)在2.6.14和2.6.29中執(zhí)行的位置不一樣。2.6.14中在do_basic_setup()函數(shù)執(zhí)行之前,而2.6.29版本中將其移植do_basic_setup()函數(shù)中執(zhí)行,怎么講呢?高版本中將populate_rootfs()移到do_initcalls()中執(zhí)行,也就是編譯的時(shí)候?qū)⒑瘮?shù)放在了.initcallrootfs.init段中。
CONFIG_BLK_DEV_RAM – 該宏被定義,表明內(nèi)核支持ramdisk,make menuconfig中需要選中RAM disk support一項(xiàng)。
CONFIG_BLK_DEV_INITRD – 該宏被定義,表明內(nèi)核有能力在內(nèi)核加載階段就能裝入RAMDISK,并運(yùn)行其中的內(nèi)容,make menuconfig中需要選中initial RAM disk(initrd) support一項(xiàng)。
下面是各種initrd的不同稱呼:
image-initrd : 老式塊設(shè)備的initrd,也叫傳統(tǒng)的文件鏡像格式的initrd
initramfs : 和內(nèi)核編譯到一起的cpio格式的initrd
cpio-initrd : cpio格式的獨(dú)立文件的initrd

populate_rootfs函數(shù)分析
static int __init populate_rootfs(void)
{
char *err = unpack_to_rootfs(__initramfs_start,
__initramfs_end - __initramfs_start, 0);
/***
編譯進(jìn)內(nèi)核的initramfs位于段.init.ramfs中,用全局變量__initramfs_start和__initramfs_end分別指向這個(gè)數(shù)據(jù)段的內(nèi)存起始地址和結(jié)束地址(虛擬地址)
所以這里了如果__initramfs_end - __initramfs_start等于0,那么unpack_to_rootfs函數(shù)不會做任何事情,直接退出。系統(tǒng)認(rèn)為該initrd不是initramfs文件,所以需要到后面去處理。
***/
if (err)
panic(err);
// 判斷是否加載了initrd,bootlaoder會將initrd加載到內(nèi)存地址initrd_start,// 前面已經(jīng)說到了如何給內(nèi)核傳遞這些參數(shù)
if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAM
// CONFIG_BLK_DEV_RAM和CONFIG_BLK_DEV_RAM經(jīng)常是同時(shí)定義
int fd;
printk(KERN_INFO "checking if image is initramfs...");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 1);
/***
判斷加載的是不是cpio-initrd。實(shí)際上 unpack_to_rootfs有兩個(gè)功能一個(gè)是釋放cpio包,另一個(gè)就是判斷是不是cpio包, 這是通過最后一個(gè)參數(shù)來區(qū)分的, 0:釋放 1:查看。
***/
if (!err) {
// 如果是cpio-initrd則利用函數(shù)unpack_to_rootfs將其內(nèi)容釋放
// 到rootfs中
printk(" it is\n");
unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 0);
free_initrd(); // 釋放掉原來存放cpio-initrd的內(nèi)存空間
return 0;
}
/***
如果執(zhí)行到這里,說明這是舊的塊設(shè)備格式的initrd。那么首先在前面掛載的根目錄rootfs上創(chuàng)建一個(gè)initrd.image文件,再把initrd_start到initrd_end的內(nèi)容寫入到/initrd.image中,最后釋放initrd占用的內(nèi)存空間(它的副本已經(jīng)保存到/initrd.image中了。)。
***/
printk("it isn't (%s); looks like an initrd\n", err);
fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);
if (fd >= 0) {
sys_write(fd, (char *)initrd_start,
initrd_end - initrd_start);
sys_close(fd);
free_initrd();
}
#else
// 如果沒有定義支持image-initrd的宏,那么直接通過下面的代碼來處理cpio-initrd
// 實(shí)際上和上面處理cpio-initrd是一樣的
printk(KERN_INFO "Unpacking initramfs...");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 0);
if (err)
panic(err);
printk(" done\n");
free_initrd();
#endif
}
return 0;
}
rootfs_initcall(populate_rootfs);
上面的populate_rootfs()執(zhí)行完后,如果是initramfs和cpio-initrd的話,都已經(jīng)將他們釋放到了前面初始化完成的rootfs中去了,那么根目錄下肯定會出現(xiàn)init文件。而如果是image-initrd,那么只會在rootfs根目錄下出現(xiàn)一個(gè)initrd.image文件。
所以就下來的處理就更加不一樣了。
static int __init kernel_init(void * unused)
{

if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
// cmdline中存在rdinit=xxx時(shí),ramdisk_execute_command才不為NULL

if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
/***
這里嘗試訪問ramdisk_execute_command,默認(rèn)為/init,如果訪問失敗,說明根目錄上不存在這個(gè)文件。于是調(diào)用prepare_namespace(),進(jìn)一步檢查是不是舊的塊設(shè)備的initrd。
(在這種情況下,還是一個(gè)塊設(shè)備鏡像文件/initrd.image,所以訪問/init文件失敗。)。
如果是initramfs或者cpio-initrd就會直接執(zhí)行init_post()函數(shù),然后在后面執(zhí)行/init文件
***/
init_post();
return 0;
}
五、老式塊設(shè)備的initrd的處理函數(shù)prepare_namespace()分析
cmdline傳了里的參數(shù)需要包括: 
root=/dev/mtdblockx rw 或者{root=/dev/ramx } init=/initrc
謹(jǐn)記root=指定的設(shè)備是最終的真實(shí)文件系統(tǒng)的。在處理image-initrd的時(shí)候指定了它,那么系統(tǒng)就會認(rèn)為你這個(gè)initrd就是我的真實(shí)文件系統(tǒng)了,會跳過很多步驟不執(zhí)行的。
void __init prepare_namespace(void)
{
int is_floppy;

if (root_delay) {
printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
root_delay);
ssleep(root_delay);
}

wait_for_device_probe();

md_run_setup();

if (saved_root_name[0]) {
// 如果cmdline有傳遞root=xxx,那么這里就會將其設(shè)備號保存下來
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
/***
將存儲真實(shí)文件系統(tǒng)的設(shè)備的設(shè)備號保存到ROOT_DEV中,如果cmdline傳遞的是/dev/ramx話,這里的ROOT_DEV = Root_RAM0,如果不是,那么是什么就是什么,后面會根據(jù)這個(gè)做出不同的事情
***/
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
/***
saved_root_name全局?jǐn)?shù)組保存的是由命令行傳進(jìn)來的root=的值,比如你傳遞了root=/dev/ramx或者root=/dev/mtdblockx rw,那么上面的if條件成立,那么就通過name_to_dev_t(root_device_name)函數(shù)獲得該設(shè)備的設(shè)備號ROOT_DEV。否則就跳過該段不執(zhí)行。
***/

if (initrd_load()) /*** 詳見后面注釋 ***/
/***
加載老式塊設(shè)備的initrd,這里也分兩種情況,一種是將該initrd作為真實(shí)文件系統(tǒng)返回1另外一種就是作為一種過渡的文件系統(tǒng)而已,此時(shí)返回0。
***/
goto out;

/***
如果要使用老式塊設(shè)備的initrd作為真實(shí)的文件系統(tǒng)的話,需要在cmdline傳入一下參數(shù):root=/dev/ramx init=/initrc,同時(shí)在內(nèi)核編譯的時(shí)候需要打開支持ramdisk和initrd
***/
/* wait for any asynchronous scanning to complete */
if ((ROOT_DEV == 0) && root_wait) {
printk(KERN_INFO "Waiting for root device %s...\n",
saved_root_name);
while (driver_probe_done() != 0 ||
(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
msleep(100);
async_synchronize_full();
}/* 如果root=正常傳遞,該if不會成立 */

is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
/***
如果cmdline傳遞了正確的root=/dev/ramx的話,mount_root()中直接創(chuàng)建一個(gè)Root_RAM0類型的/dev/root節(jié)點(diǎn)(前面initrd_load()中將image-initrd已經(jīng)導(dǎo)入到了Root_RAM0ramdisk中了),所以mount_root()中就可以直接使用。
***/
if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;

mount_root(); // 將ROOT_DEV設(shè)備上的真實(shí)文件系統(tǒng)mount到rootfs的/root目錄中
out:
// 注意:上面的過程都已經(jīng)將真實(shí)文件系統(tǒng)掛載到了/root(這里的/還是rootfs),
// 并且當(dāng)前目錄為/root
sys_mount(".", "/", NULL, MS_MOVE, NULL);
// mount當(dāng)前目錄為根目錄,覆蓋掉原來的rootfs
sys_chroot(".");
// 切換當(dāng)前目錄為程序執(zhí)行所參考的根目錄位置,至此,真實(shí)文件系統(tǒng)
// 掛載完畢
}

+++++++++++++++++++++++++++++++++++++++++++++++++++++++
int __init initrd_load(void)
{
if (mount_initrd) {
create_dev("/dev/ram", Root_RAM0);
// 建立一個(gè)Root_RAM0類型的/dev/ram設(shè)備節(jié)點(diǎn),實(shí)際上Root_RAM0設(shè)備就是
// 一ramdisk

if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
// rd_load_image()將文件initrd.image加載進(jìn)了/dev/ram0中去了
sys_unlink("/initrd.image"); // 刪除文件initrd.image
handle_initrd(); // handle_initrd()函數(shù)負(fù)責(zé)對initrd進(jìn)行具體的處理
return 1; /* image-initrd 作為中間過渡的文件系統(tǒng) */
}

/***
/initrd.image文件保存的就是image-initrd,rd_load_image函數(shù)執(zhí)行具體的加載操作,
將image-nitrd的文件內(nèi)容釋放到設(shè)備類型為Root_RAM0的ramdisk中去(節(jié)點(diǎn)名為ram)。判斷ROOT_DEV!=Root_RAM0的含義是(ROOT_DEV不能=0,否則mount的時(shí)候會出錯(cuò)),正如前面所說,如果你在bootloader里配置了root=/dev/ramx,則實(shí)際上真正的根設(shè)備就是這個(gè)initrd了,所以就不把它作為initrd處理 ,而是作為真實(shí)文件系統(tǒng)來處理。
***/
}
sys_unlink("/initrd.image");// 刪除文件initrd.image
return 0; /* image-initrd作為真實(shí)的文件系統(tǒng) */
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void __init handle_initrd(void)
{
int error;
int pid;

real_root_dev = new_encode_dev(ROOT_DEV);
// real_root_dev全局變量保存的是存放realfs的設(shè)備的設(shè)備號
create_dev("/dev/root.old", Root_RAM0);
// 建立一個(gè)Root_RAM0類型的/dev/root.old設(shè)備節(jié)點(diǎn),實(shí)際上就是一ramdisk
// 訪問root.old和上層函數(shù)中創(chuàng)建的ram節(jié)點(diǎn)是一樣的,對應(yīng)的是同一設(shè)備,內(nèi)容一樣
/* mount initrd on rootfs's /root */
mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
// 將/dev/root.old中的initrd文件系統(tǒng)掛載到了rootfs的/root目錄下
sys_mkdir("/old", 0700); /* 在rootfs的根目錄下創(chuàng)建old目錄 */
root_fd = sys_open("/", 0, 0);
// 通過這種方式保存原根目錄的描述符
old_fd = sys_open("/old", 0, 0);
// 通過這種方式保存/old的描述符
/* move initrd over / and chdir/chroot in initrd root */
sys_chdir("/root");
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
// 進(jìn)入/root目錄,將當(dāng)前目錄mount為根目錄,然后切換當(dāng)前目錄為程序執(zhí)行所
// 參考的根目錄位置

/*
* In case that a resume from disk is carried out by linuxrc or one of
* its children, we need to tell the freezer not to wait for us.
*/
current->flags |= PF_FREEZER_SKIP;

pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
// 啟動(dòng)一進(jìn)程執(zhí)行/linuxrc,并等待其執(zhí)行完??梢钥闯銮懊鎴?zhí)行sys_chroot(".")的意義
if (pid > 0)
while (pid != sys_wait4(-1, NULL, 0, NULL))
yield();

current->flags &= ~PF_FREEZER_SKIP;

/* move initrd to rootfs' /old */
sys_fchdir(old_fd);
sys_mount("/", ".", NULL, MS_MOVE, NULL);
// 進(jìn)入old_fd描述的目錄,再將現(xiàn)在的根目錄mount到原來根目錄下的old目錄中
/* switch root and cwd back to / of rootfs */
sys_fchdir(root_fd);
sys_chroot(".");
// 進(jìn)入真正的根目錄,然后切換當(dāng)前目錄為程序執(zhí)行所參考的根目錄位置,也就是根
// 目錄還原, 但是真實(shí)的文件系統(tǒng)還沒有掛載
sys_close(old_fd);
sys_close(root_fd);

if (new_decode_dev(real_root_dev) == Root_RAM0) {
sys_chdir("/old");
return;
}
// 取真實(shí)文件系統(tǒng)所在設(shè)備的設(shè)備號
ROOT_DEV = new_decode_dev(real_root_dev);
mount_root();
// 將真實(shí)文件系統(tǒng)掛載到/root目錄中
// 注意mount_root()實(shí)際調(diào)用了函數(shù)mount_block_root()函數(shù)來實(shí)現(xiàn)mount,之后會
// chdir(/root),否則后面返回上上層函數(shù)有個(gè)地方可能看不明白

printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
// 將/old移植/root/initrd中去
if (!error)
printk("okay\n");
else {
int fd = sys_open("/dev/root.old", O_RDWR, 0);
if (error == -ENOENT)
printk("/initrd does not exist. Ignored.\n");
else
printk("failed\n");
printk(KERN_NOTICE "Unmounting old root\n");
sys_umount("/old", MNT_DETACH);
// 移動(dòng)不成功就將/old目錄卸載掉
printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
if (fd < 0) {
error = fd;
} else {
error = sys_ioctl(fd, BLKFLSBUF, 0);
sys_close(fd);
}
printk(!error ? "okay\n" : "failed\n");
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
至此,通過image-initrd將真實(shí)的文件系統(tǒng)掛載起來了



附文: 
rootfs_initialize.c -- rootfs初始化部分的代碼分析注解
rootfs初始化調(diào)用層次.txt

參考網(wǎng)址:
http://wiki./ramfs
http://linux./Linuxwendang/xitongguanliyuan/1926.html
http://blog.csdn.net/knock/archive/2010/02/02/5280255.aspx
http://www./linux/fudao/20090506/101701272.html
http://my.donews.com/tangfl/2007/12/17/linux_tmpfs_ramfs/
kouu’s home 頁面回收
http://www.ibm.com/developerworks/cn/linux/l-k26initrd/
文中的網(wǎng)址鏈接也為參考部分

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多