根檔案系統
目錄
title: 根檔案系統
tags: linux
date: 2018-09-19 19:56:05
---
根檔案系統
檔案系統概念
檔案系統是一些程式碼,是一套軟體,這套軟體的功能就是對儲存裝置的扇區進行管理,將這些扇區的訪問變成了對目錄和檔名的訪問。我們在上層按照特定的目錄和檔名去訪問一個檔案時,檔案系統會將這個目錄+檔名轉換成對扇區號的訪問。
- 虛擬檔案系統介面(VFS)是linux定義的一個檔案系統的統一介面,這樣各種型別的檔案系統都採用一樣的介面給使用者
- 虛擬檔案系統儲存在RAM裡的,沒有實際的裝置(ROM)與之對應
- 實際檔案系統有實際的儲存裝置(ROM)與之對應,又可分為遠端檔案系統和本地檔案系統
- 根檔案系統處於檔案系統的最上層,其很重要的作用是用來掛載其他檔案系統。根檔案系統可以是虛擬檔案系統也可以是實際檔案系統,只要條件支援.
- 檔案系統格式 linux支援包括ext2,ext3,vfat,jffs,ramfs,nfs等檔案系統
- jffs2:主要用於nor型flash,特點是可讀寫,支援資料壓縮的日誌型檔案系統。
- yaffs/yaffs2:主要用於nand型flash,支援跨平臺。
- cramfs:只讀的壓縮檔案系統。可用於兩種flash。
- ramdisk:基於ram的檔案系統。是將一部分固定大小的記憶體當做塊裝置來用。它並非是一個實際的檔案系統,而是一種將實際的檔案系統裝入記憶體的機制。將一些經常訪問而又無需更改的檔案通過ramdisk放在記憶體中,可以明顯的提高系統的效能。
- initramfs:基於ram的檔案系統。initramfs出現在2.6核心中,它類似於tmpfs,是一種基於記憶體的檔案系統,它的使用不需要建立記憶體塊裝置。增加檔案到ramfs會自動配置更多的記憶體,並刪除或截去檔案以釋放記憶體。(若ramdisk沒有滿,已被佔用的額外的記憶體也不能用來做其它事情,若ramdisk滿了,但其它仍有閒置的記憶體,也必須重新格式化以後才能擴充套件使用)
- nfs:是由sun開發的一種在不同機器之間通過網路共享檔案的技術。在嵌入式linux系統的開發除錯階段,可以利用該技術在主機上建立基於nfs的根檔案系統,掛載到嵌入式裝置,可以很方便的修改根檔案系統的內容。
啟動流程
核心通過一系列初始化之後,掛載根檔案系統來執行應用程式. 所謂根檔案系統,需要提前按照一定的檔案系統的格式化,並放入相應的內容資料.Kernel 根據啟動引數去尋找根檔案系統的位置去mount
根檔案系統
ramdisk啟動
- 製作ramdisk檔案系統壓縮包
- 然後將這個檔案壓縮包通過bootloader下載到記憶體中
- 當系統啟動的時候
- 通過uboot的bootargs環境變數來傳遞啟動引數,修改為bootargs=initrd=0x31000000,0x200000 root=/dev/ram rw init=/linuxrc console=ttySAC0 mem=64,以此指定從ramdisk啟動,ramdisk壓縮檔案起始地址在記憶體地址0x31000000處,檔案大小為0x200000。
- 也可以通過修改核心配置Default kernel command string為”initrd=0x31000000,0x200000 root=/dev/ram rw init=/linuxrc console=ttySAC0 mem=64”
flash啟動
檔案系統存在FLASH
,核心根據命令列引數root=/dev/xxx
,直接mount
。
問題? 在root檔案系統本身還不存在的情況下,核心如何根據/dev/xxx
來找到對應的裝置呢?
解答: 根檔案系統和其他檔案系統的mount方式是不一樣的,核心通過直接解析裝置的名稱來獲得裝置的主、從裝置號,然後就可以訪問對應的裝置驅動了。所以在init/main.c
中有很長一串的root_dev_names
(如hda,hdab,sda,sdb,nfs,ram,mtdblock……),通過這個表就可以根據裝置名稱得到裝置號。注意,bootloader
或核心中設定的啟動引數root=/dev/xxx
只是一個代號,實際的根檔案系統中不一定存在這個裝置檔案!
拿ARM來說,在檔案arch/arm/plat-s3c24xx/common-smdk.c
中有分割槽表:
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "Boot",
.size = 0x00020000,
.offset = 0,
},
[1] = {
.name = "Kernel",
.size = 0x00240000,
.offset = 0x00040000,
},
[2] = {
.name = "RootFileSystem",
.size = 0x02000000,
.offset = 0x00280000,
},
[3] = {
.name = "ExtendFileSystem",
.size = 0x05d80000,
.offset = 0x02280000,
}
};
initramfs
initramfs將根目錄直接編譯到linux核心映象中 ,這種方法不同於前面兩種方法需要在uboot啟動引數中指定ramdisk載入到記憶體的地址或者檔案系統在flash中的分割槽,由於已經將根目錄編譯到linux的.init.ramfs段中了,所以啟動的時候直接將linux核心映象的.init.ramfs段的內容解壓到系統的rootfs中。
initrd
啟動時用到initrd來mount根檔案系統。注意理解ramdisk和initrd這兩個概念,其實ramdisk只是在ram上實現的塊裝置,類似與硬碟操作,但有更快的讀寫速度,它可以在系統執行的任何時候使用,而不僅僅是用於啟動;
initrd(boot loader initialized RAM disk)可以說是啟動過程中用到的一種機制,具體的實現過程也使用ramdisk技術。就是在裝載linux之前,bootloader可以把一個比較小的根檔案系統的映象裝載在記憶體的某個指定位置,姑且把這段記憶體稱為initrd(這裡是initrd所佔的記憶體,不是ramdisk,注意區別),然後bootloader通過傳遞引數的方式告訴核心initrd的起始地址和大小(也可以把這些引數編譯在核心中),在啟動階段就可以暫時的用initrd來mount根檔案系統。initrd的最初的目的是為了把kernel的啟動分成兩個階段:在kernel中保留最少最基本的啟動程式碼,然後把對各種各樣硬體裝置的支援以模組的方式放在initrd中,這樣就在啟動過程中可以從initrd所mount的根檔案系統中裝載需要的模組。這樣的一個好處就是在保持kernel不變的情況下,通過修改initrd中的內容就可以靈活的支援不同的硬體。在啟動完成的最後階段,根檔案系統可以重新mount到其他裝置上,但是也可以不再 重新mount(很多嵌入式系統就是這樣)。 initrd的具體實現過程是這樣的:bootloader把根檔案系統映象裝載到記憶體指定位置,把相關引數傳遞給核心,核心啟動時把initrd中的內容複製到ramdisk中(ram0),把initrd佔用的記憶體釋放掉,在ram0上mount根檔案系統。從這個過程可以看出,核心需要對同時對ramdisk和initrd的支援(這種需要都編入核心,不能作為模組)。
雞生蛋蛋生雞
核心剛啟動時,磁碟裝置、網路裝置都還沒有被驅動起來,所以無法訪問磁碟,沒法給磁碟啟用對應的檔案系統。那趕緊安裝磁碟驅動程式,網路驅動程式呀,怎麼不載入呢?因為磁碟種類太多了,沒法把所有的驅動都編譯到核心裡頭,那樣核心得變得多大呀,所以就只能把這些驅動程式編譯成模組的方式,在核心載入的時候現場判斷當前用的是什麼磁碟再加相應的磁碟驅動模組。那就載入磁碟驅動模組呀,等什麼呢?原因是編譯成驅動模組後,在這個階段壓根就沒法載入!還沒檔案系統呢,怎麼載入驅動模組?結果啟用檔案系統的前提是磁碟的驅動程式已經載入,而驅動程式的載入的前提是已經有檔案系統存在,這就成了雞生蛋,蛋生雞的問題,怎麼破?想到核心載入的時候,RAM其實已經可用了,那就基於RAM建立一個臨時檔案系統吧,這個臨時的檔案系統自己掛載到自己身上,然後我們指定這個檔案系統為根檔案系統,這樣就有了起步的檔案系統啦,藉助這個臨時的檔案系統把磁碟驅動模組、網路驅動模組載入上,這樣就可以掛載實際的檔案系統啦,有了實際的檔案系統之後再把這個實際的檔案系統指定為根檔案系統,這就好啦,然後其他的各式各樣的檔案系統就可以陸陸續續的掛載在這個根檔案系統下了。
回到之前的一個問題,怎麼建立一個基於RAM的虛擬檔案系統?首先,在編譯核心得時候就編譯一個很精簡的虛擬檔案系統進去,然後核心在啟動的時候先註冊一個rootfs這個虛擬檔案系統,然後掛載這個虛擬檔案系統,那rootfs這個虛擬檔案還是個空的,得給裡頭放點東西呀,放什麼呢?就放編譯進核心裡頭的那個很精簡的虛擬檔案系統裡的內容。怎麼內容放進rootfs裡去?方法簡單粗暴,直接把編譯進核心裡頭的那個很精簡的虛擬檔案系統裡的內容解壓到rootfs裡,這個過程叫填充rootfs。