版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第7章嵌入式文件系統(tǒng)
7.1文件系統(tǒng)基本概念
7.2虛擬文件系統(tǒng)VFS
7.3基于Flash的文件系統(tǒng) 7.4基于RAM的文件系統(tǒng)
7.5Busybox
Linux的一個最重要特點就是可以支持許多不同的文件系統(tǒng)。這使Linux非常靈活,能夠與許多其他的操作系統(tǒng)共存。Linux支持的常見的文件系統(tǒng)有:JFS、ReiserFS、Ext、Ext2、Ext3、ISO9660、XFS、Minx、MSDOS、UMSDOS、VFAT、NTFS、HPFS、NFS、SMB、SysV、PROC等。隨著時間的推移,Linux支持的文件系統(tǒng)數(shù)還會增加。7.1文件系統(tǒng)基本概念
Linux是通過把系統(tǒng)支持的各種文件系統(tǒng)鏈接到一個單獨的樹形層次結(jié)構(gòu)中,來實現(xiàn)對多文件系統(tǒng)的支持。該樹形層次結(jié)構(gòu)把文件系統(tǒng)表示成一個整個的獨立實體。無論什么類型的文件系統(tǒng),都被裝配到某個目錄上,由被裝配的文件系統(tǒng)的文件覆蓋該目錄原有的內(nèi)容。該目錄被稱為裝配目錄或裝配點。在文件系統(tǒng)卸載時,裝配目錄中原有的文件才會顯示出來。7.1.1嵌入式根文件系統(tǒng)
Linux下的根文件系統(tǒng)目錄結(jié)構(gòu)如下:
目錄習(xí)慣用法
bin 用戶命令所在目錄
dev 硬件設(shè)備文件及其他特殊文件
etc 系統(tǒng)配置文件,包括啟動文件等
home多用戶主目錄
lib 鏈接庫文件目錄
mnt 裝配點,用于裝配臨時文件系統(tǒng)或其他的文件
系統(tǒng)
opt 附加的軟件套件目錄
proc 虛擬文件系統(tǒng),用來顯示內(nèi)核及進程信息
root root用戶主目錄
sbin 系統(tǒng)管理員命令目錄
tmp 臨時文件目錄
usr 用戶命令目錄
var 監(jiān)控程序和工具程序所存放的可變數(shù)據(jù)
對于用途單一的嵌入式系統(tǒng),上述的一些用于多用戶的目錄可以省略,例如/home、/opt、/root目錄等。而/bin、/dev、/etc、/lib、/proc、/sbin和/usr目錄,幾乎是每個系統(tǒng)必備的目錄,也是不可或缺的目錄。7.1.2嵌入式系統(tǒng)存儲設(shè)備及其管理機制分析
構(gòu)建適用于嵌入式系統(tǒng)的Linux文件系統(tǒng),必然會涉及到兩個關(guān)鍵問題,一是文件系統(tǒng)類型的選擇,關(guān)系到文件系統(tǒng)的讀/寫性能、大??;另一個是根文件系統(tǒng)內(nèi)容的選擇,關(guān)系到根文件系統(tǒng)所能提供的功能及文件大小。
1.閃存技術(shù)
目前在嵌入式系統(tǒng)應(yīng)用的Flash存儲器從結(jié)構(gòu)上大體可以分為AND、NAND、NOR和DiNOR等幾種。其中NOR和DiNOR的特點為相對電壓低、隨機讀取快、功耗低、穩(wěn)定性高,而NAND和AND的特點為容量大、回寫速度快、芯片面積小。NOR和NANDFlash的應(yīng)用最為廣泛,除了在嵌入式設(shè)備上得到廣泛的應(yīng)用外,在CompactFlash、SecureDigital、PCCards、MMC存儲卡以及USB閃盤存儲器市場都占用較大的份額。
NOR型閃存可以直接讀取芯片內(nèi)儲存的數(shù)據(jù),因而速度比較快,但是價格較高。NOR型芯片,地址線與數(shù)據(jù)線分開,所以NOR型芯片可以像SRAM一樣連在數(shù)據(jù)線上,對NOR芯片可以“字”為基本單位操作,因此傳輸效率很高,應(yīng)用程序可以直接在Flash內(nèi)運行,不必再把代碼讀到系統(tǒng)RAM中運行。NOR型閃存與SRAM的最大不同在于寫操作需要經(jīng)過擦除和寫入兩個過程。
NAND型閃存芯片共用地址線與數(shù)據(jù)線,內(nèi)部數(shù)據(jù)以塊為單位進行存儲,直接將NAND芯片做啟動芯片比較難。NAND閃存是連續(xù)存儲介質(zhì),適合存放大文件。擦除NOR器件時是以64~128?KB的塊進行的,執(zhí)行一個寫入/擦除操作的時間為5?s;擦除NAND器件是以8~32?KB的塊進行的,執(zhí)行相同的操作最多只需要4?ms。NANDFlash的單元尺寸幾乎是NOR器件的一半,由于生產(chǎn)過程更為簡單,NAND結(jié)構(gòu)可以在給定的模具尺寸內(nèi)提供更高的容量,也就相應(yīng)地降低了價格。NORFlash占據(jù)了小容量閃存市場的大部分,而NANDFlash只是用在大容量產(chǎn)品當(dāng)中,這也說明NOR主要應(yīng)用在代碼存儲介質(zhì)中,NAND適合于數(shù)據(jù)存儲。在NAND閃存中每個塊的最大擦/寫次數(shù)是一百萬次,而NOR的擦/寫次數(shù)是十萬次。NAND存儲器除了具有10?:?1的塊擦除周期優(yōu)勢,典型的NAND塊尺寸是NOR器件的1/9,每個NAND存儲器塊在給定的時間內(nèi)的刪除次數(shù)要少一些。這兩種結(jié)構(gòu)性能上的異同(見表7-1)分析如下:
(1)?NOR的讀取速度比NAND稍快一些。
(2)?NAND的寫入速度比NOR快很多。
(3)?NAND的擦除速度遠比NOR快。
(4)?NAND的擦除單元更小,相應(yīng)的擦除電路也更加簡單。
(5)?NAND閃存中每個塊的最大擦/寫次數(shù)是一百萬次,而NOR的擦/寫次數(shù)是十萬次。此外,NAND的實際應(yīng)用方式要比NOR復(fù)雜得多。NOR可以直接使用,并在上面直接運行代碼。而NAND需要I/O接口,因此使用時需要驅(qū)動程序。不過,當(dāng)今流行的操作系統(tǒng)對NANDFlash都提供驅(qū)動支持,Linux內(nèi)核也對NANDFlash提供了很好的支持。由于以上Flash的特性,在嵌入式設(shè)備中,一般會把只讀屬性的映像文件,如啟動引導(dǎo)程序Blob、內(nèi)核、文件系統(tǒng)文件存放在NORFlash中,而把一些讀/寫類的文件,如用戶應(yīng)用程序等存放在NANDFlash中。出于成本的考慮,很多廠家會選用低容量、昂貴的NORFlash存儲啟動引導(dǎo)程序和內(nèi)核,而把文件系統(tǒng)存放在NANDFlash中。表7-1NOR閃存與NAND閃存的比較
NORFlashNANDFlash接口時序同SRAM,易使用地址/數(shù)據(jù)線復(fù)用,數(shù)據(jù)位較窄讀取速度較快讀取速度較慢擦除速度慢,以64~128?KB的塊為單位擦除速度快,以8~32?KB的塊為單位寫入速度慢(因為一般要先擦除)寫入速度快隨機存取速度較快,支持XIP(eXecuteInPlace,芯片內(nèi)執(zhí)行),適用于代碼存儲。在嵌入式系統(tǒng)中,常用于存放引導(dǎo)程序、根文件系統(tǒng)等順序讀取速度較快,隨機存取速度慢,適用于數(shù)據(jù)存儲(如大容量的多媒體應(yīng)用)。在嵌入式系統(tǒng)中,常用于存放用戶文件系統(tǒng)等單片容量較小,1~32?MB單片容量較大,8~128?MB,提高了單元密度最大擦/寫次數(shù)為十萬次
2.驅(qū)動
所有嵌入式系統(tǒng)的啟動都至少需要使用某種形式的永久性存儲設(shè)備,需要合適的驅(qū)動程序,當(dāng)前在嵌入式Linux中有三種常用的塊驅(qū)動程序可以選擇。
1)?Blkmem驅(qū)動層
Blkmem驅(qū)動是為mCLinux專門設(shè)計的,也是最早的一種塊驅(qū)動程序,現(xiàn)在仍然有很多嵌入式Linux操作系統(tǒng)選用它作為塊驅(qū)動程序,尤其是在mCLinux中。Blkmem相對來說是最簡單,而且只支持建立在NOR型Flash和RAM中的根文件系統(tǒng)。使用Blkmem驅(qū)動,建立Flash分區(qū)配置比較困難,這種驅(qū)動程序為Flash提供了一些基本擦除/寫入操作。
2)?RAMdisk驅(qū)動層
RAMdisk驅(qū)動層通常應(yīng)用在標準Linux中無盤工作站的啟動,對Flash存儲器并不提供任何的直接支持,RAMdisk
就是在開機時,把一部分的內(nèi)存虛擬成塊設(shè)備,并且把之
前所準備好的檔案系統(tǒng)映像解壓縮到該RAMdisk環(huán)境中。當(dāng)在Flash中放置壓縮的文件系統(tǒng)時,可以將文件系統(tǒng)解壓到
RAM,使用RAMdisk驅(qū)動層支持保持在RAM中的文件系統(tǒng)。
3)?MTD驅(qū)動層
Linux內(nèi)核納入了MTD(MemoryTechnologyDevice)子系統(tǒng),從而提供了統(tǒng)一的接口,讓底層的MTD芯片驅(qū)動程序無縫地與較高層接口組合在一起。
7.1.3嵌入式Linux中的MTD驅(qū)動層
要在Flash存儲器中使用CramFS或YAFFS文件系統(tǒng),就離不開MTD驅(qū)動程序?qū)拥闹С?。MTD是Linux中的一個存儲設(shè)備通用接口層,雖然也可以建立在RAM上,但是專為基于Flash的設(shè)備而設(shè)計的。MTD包含特定Flash芯片的驅(qū)動程序,并且越來越多的芯片驅(qū)動正被添加進來。用戶要使用MTD,首先要選擇適合的Flash芯片驅(qū)動。Flash芯片驅(qū)動向上層提供讀、寫、擦除等基本的Flash操作方法。MTD對這些操作進行封裝后向用戶層提供MTDchar和MTDblock類型的設(shè)備。
MTDchar類型的設(shè)備包括/dev/mtd0、/dev/mtd1等,這些設(shè)備文件提供對Flash的原始字符訪問;MTDblock類型的設(shè)備包括/dev/mtdblock0、/dev/mtdblockl等,這些設(shè)備是將Flash模擬成塊設(shè)備,因此可以在這些模擬的塊設(shè)備上創(chuàng)建YAFFS或CramFS等格式的文件系統(tǒng)。
另外,MTD支持CFI(CommonFlashInterface)接口,利用CFI接口可以在一塊Flash存儲芯片上創(chuàng)建多個Flash分區(qū)。每一個分區(qū)作為一個MTDblock設(shè)備,可以把系統(tǒng)軟件和數(shù)據(jù)等分配到不同的分區(qū)上,同時可以在不同的分區(qū)上采用不同的文件系統(tǒng)格式。
1.LinuxMTD介紹
MTD內(nèi)存技術(shù)設(shè)備是用于訪問Memory設(shè)備(ROM、Flash)的Linux子系統(tǒng)。MTD的主要目的是為了使新的存貯設(shè)備的驅(qū)動更加簡單,在硬件和上層之間提供了一個抽象的接口。MTD的所有源代碼在/drivers/mtd子目錄下。一般可將CFI接口的MTD設(shè)備分為四層(從設(shè)備節(jié)點直到底層硬件驅(qū)動),這四層從上到下依次是:設(shè)備節(jié)點層(包括字符設(shè)備節(jié)點層和塊設(shè)備節(jié)點層)、MTD設(shè)備層(包括MTD塊設(shè)備層和MTD字符設(shè)備層)、MTD原始設(shè)備層和Flash硬件驅(qū)動層,見圖7-1。圖7-1MTD設(shè)備層
1)?Flash硬件驅(qū)動層
Flash硬件驅(qū)動層負責(zé)在init時驅(qū)動Flash硬件,LinuxMTD設(shè)備的NORFlash芯片驅(qū)動遵循CFI接口標準,其驅(qū)動程序位于drivers/mtd/chips子目錄下。NANDFlash的驅(qū)動程序則位于/drivers/mtd/nand子目錄下。
2)?MTD原始設(shè)備層
MTD原始設(shè)備層由兩部分組成,一部分是MTD原始設(shè)備的通用代碼,另一部分是各個特定的Flash數(shù)據(jù),例如分區(qū)。用于描述MTD原始設(shè)備的數(shù)據(jù)結(jié)構(gòu)是mtd_info,其中定義了關(guān)于MTD的數(shù)據(jù)和操作函數(shù)。mtd_table(mtdcore.c)是所有MTD原始設(shè)備的列表,mtd_part(mtd_part.c)是用于表示MTD原始設(shè)備分區(qū)的結(jié)構(gòu),其中包含了mtd_info。因為每一個分區(qū)都是被看成一個MTD原始設(shè)備加在mtd_table中的,所以mtd_part.mtd_info中的大部分數(shù)據(jù)都從該分區(qū)的主分區(qū)mtd_part->master中獲得。
在drivers/mtd/maps/子目錄下存放的是特定的Flash數(shù)據(jù),每一個文件都描述了一塊開發(fā)板上的Flash。其中調(diào)用add_mtd_device()、del_mtd_device()建立/刪除mtd_info結(jié)構(gòu)并將其加入/刪除mtd_table(或者調(diào)用add_mtd_partition()、del_mtd_partition()(mtdpart.c)建立/刪除mtd_part結(jié)構(gòu)并將mtd_part.mtd_info加入/刪除mtd_table)。
3)?MTD設(shè)備層
基于MTD原始設(shè)備層,Linux系統(tǒng)可以定義MTD的塊設(shè)備(主設(shè)備號31)層和字符設(shè)備(設(shè)備號90)層。MTD字符設(shè)備的定義在mtdchar.c中實現(xiàn),通過注冊一系列fileoperation函數(shù)(lseek、open、close、read、write)完成。MTD塊設(shè)備則是定義了描述MTD塊設(shè)備的結(jié)構(gòu)mtdblk_dev,并聲明了名為mtdblks的指針數(shù)組,該數(shù)組中的每個mtdblk_dev和mtd_table中的mtd_info一一對應(yīng)。
4)?設(shè)備節(jié)點層
通過mknod在/dev子目錄下建立MTD字符設(shè)備節(jié)點(主設(shè)備號為90)層和MTD塊設(shè)備節(jié)點(主設(shè)備號為31)層,通過訪問此設(shè)備節(jié)點即可訪問MTD字符設(shè)備和塊設(shè)備。
5)?根文件系統(tǒng)層
在Bootloader中將JFFS(或JFFS2)的文件系統(tǒng)映像jffs.image(或jffs2.img)燒寫到Flash的某一個分區(qū)中,在/arch/arm/mach-your/arch.c文件的your_fixup函數(shù)中將該分區(qū)作為根文件系統(tǒng)掛載。
6)文件系統(tǒng)層
內(nèi)核啟動后,通過掛載命令可以將Flash中的其余分區(qū)作為文件系統(tǒng)掛載到掛載點上。
設(shè)備層和原始設(shè)備層的函數(shù)調(diào)用關(guān)系如下:
一個MTD原始設(shè)備可以通過mtd_part分割成數(shù)個MTD原始設(shè)備,并注冊進mtd_table,mtd_table中的每個MTD原始設(shè)備都可以被注冊成一個MTD設(shè)備,其中字符設(shè)備的主設(shè)備號為90,次設(shè)備號為0、2、4、6…(奇數(shù)次設(shè)備號為只讀設(shè)備),塊設(shè)備的主設(shè)備號為31,次設(shè)備號為0、1、2、3…?。代碼如下:mtd_notifier
mtd_notifier
字符設(shè)備
mtd_fops
塊設(shè)備
mtd_fops(mtdchar.c)
(mtdblock.c)
mtdblks設(shè)備層
register_mtd_user()
get_mtd_device()
unregister_mtd_user()
put_mtd_device()
erase_info
mtd_notifiers
mtd_table
mtd_info
mtd_part
(mtdcore.c)
(mtdpart.c)
YourFlash
(your-flash.c)
add_mtd_partitions()
del_mtd_partitions()
原始設(shè)備層
add_mtd_device()
del_mtd_device()
mtd_partition
NOR型Flash芯片驅(qū)動與MTD原始設(shè)備
所有的NOR型Flash的驅(qū)動(探測)程序在drivers/mtd/chips目錄下,一個MTD原始設(shè)備可以由一塊或者數(shù)塊相同的Flash芯片組成。假設(shè)由4塊devicetype為x8的Flash,每塊大小為8?MB,interleave為2,起始地址為0x01000000,地址相連,則構(gòu)成一個MTD原始設(shè)備(0x01000000~0x03000000),其中兩塊interleave構(gòu)成一個chip,其地址為0x01000000~0x02000000,另兩塊interleave構(gòu)成一個chip,其地址為0x02000000~0x03000000。所有組成一個MTD原始設(shè)備的Flash芯片必須是同類型的(無論是interleave還是地址相連),在描述MTD原始設(shè)備的數(shù)據(jù)結(jié)構(gòu)中也只是采用了同一個結(jié)構(gòu)來描述組成它的Flash芯片,例如:
0x03000000
0x02000000
0x01000000
每個MTD原始設(shè)備都有一個mtd_info結(jié)構(gòu),其中的priv指針指向一個map_info結(jié)構(gòu),map_info結(jié)構(gòu)中的fldrv_priv指向一個cfi_private結(jié)構(gòu),cfi_private結(jié)構(gòu)的cfiq指針指向一個cfi_ident結(jié)構(gòu),chips指針指向一個flchip結(jié)構(gòu)的數(shù)組。其中mtd_info、map_info和cfi_private結(jié)構(gòu)用于描述MTD原始設(shè)備;因為組成MTD原始設(shè)備的NORFlash相同,所以cfi_ident結(jié)構(gòu)用于描述Flash芯片的信息;而flchip結(jié)構(gòu)用于描述每個Flash芯片的專有信息(比如起始地址)。驅(qū)動還用于對DiskOnChip產(chǎn)品進行仿真和NAND閃存的管理,包括糾錯、壞塊處理和損耗平衡。
2.Linux下的文件系統(tǒng)結(jié)構(gòu)
Linux啟動時,第一個必須掛載的是根文件系統(tǒng);若系統(tǒng)不能從指定設(shè)備上掛載根文件系統(tǒng),則系統(tǒng)會出錯而退出啟動,之后可以自動或手動掛載其他的文件系統(tǒng)。因此,一個系統(tǒng)中可以同時存在不同的文件系統(tǒng)。
不同的文件系統(tǒng)類型有不同的特點,因而根據(jù)存儲設(shè)備的硬件特性、系統(tǒng)需求等有不同的應(yīng)用場合。在嵌入式Linux應(yīng)用中,主要的存儲設(shè)備為RAM(DRAM,SDRAM)和ROM(常采用Flash存儲器),常用的基于存儲設(shè)備的文件系統(tǒng)類型包括:JFFS2、YAFFS、CramFS、ROMFS、RAMdisk、Ramfs/Tmpfs等。在Linux文件系統(tǒng)中,文件用i節(jié)點來表示,目錄只是包含有一組目錄條目列表的簡單文件,而設(shè)備可以通過特殊文件上的I/O請求被訪問。
1)節(jié)點(Inodes)
每個文件都是由被稱為i節(jié)點的一個結(jié)構(gòu)來表示的。每個i節(jié)點都含有對特定文件的描述:文件類型、訪問權(quán)限、屬主、時間戳、大小、指向數(shù)據(jù)塊的指針。分配給一個文件的數(shù)據(jù)塊的地址也存儲在該文件的i節(jié)點中。當(dāng)一個用戶在該文件上請求一個I/O操作時,內(nèi)核代碼將當(dāng)前偏移量轉(zhuǎn)換成一個塊號,并使用這個塊號作為塊地址表中的索引來讀/寫實際的物理塊。圖7-2表示了一個i節(jié)點的結(jié)構(gòu)。圖7-2i節(jié)點的結(jié)構(gòu)
2)目錄
目錄是一個分層的樹結(jié)構(gòu)。每個目錄可以包含文件和子目錄。目錄是作為一個特殊的文件實現(xiàn)的。實際上,目錄是一個含有目錄條目的文件,每個條目含有一個i節(jié)點號和一個文件名。當(dāng)進程使用一個路徑名時,內(nèi)核代碼就會在目錄中搜索以找到相應(yīng)的i節(jié)點號,在文件名被轉(zhuǎn)換成了一個i節(jié)點以后,該i節(jié)點就被加載到內(nèi)存中并被隨后的請求所使用。圖7-3表示了一個目錄結(jié)構(gòu)。圖7-3目錄結(jié)構(gòu)
3)鏈接
Linux文件系統(tǒng)實現(xiàn)了鏈接(Links)的概念。幾個文件名可以與一個i節(jié)點相關(guān)聯(lián)。i節(jié)點含有一個字段,其中含有與文件的關(guān)聯(lián)數(shù)目。要增加一個鏈接只需簡單地建立一個目錄項,該目錄項的i節(jié)點號指向該i節(jié)點并增加該i節(jié)點的鏈接數(shù)即可。但刪除一個鏈接時,也即當(dāng)使用rm命令刪除一個文件名時,內(nèi)核會遞減i節(jié)點的鏈接計數(shù)值,如果該計數(shù)值等于零的話,就會釋放該i節(jié)點。
這種類型的鏈接稱為硬鏈接(HardLink),并且只能在單獨的文件系統(tǒng)內(nèi)使用,也即不可能創(chuàng)建一個跨越文件系統(tǒng)的硬鏈接。而且,硬鏈接只能指向文件,為了防止目錄樹的循環(huán),不能創(chuàng)建目錄的硬鏈接。
在大多數(shù)Linux文件系統(tǒng)中還有另外一種鏈接,即符號鏈接(SymbolicLink)。符號鏈接僅是含有一個文件名的簡單文件。在從路徑名到i節(jié)點的轉(zhuǎn)換中,當(dāng)內(nèi)核遇到一個符號鏈接時,就用該符號鏈接文件的內(nèi)容替換鏈接的文件名,也即用目標文件的名稱來替換,并重新開始路徑名的翻譯工作。由于符號鏈接并沒有指向i節(jié)點,因此就有可能創(chuàng)建一個跨越文件系統(tǒng)的符號鏈接。符號鏈接可以指向任何類型的文件,甚至是一個不存在的文件。由于沒有與硬鏈接相關(guān)的限制,因此符號鏈接非常有用。然而,符號鏈接會占用一部分磁盤空間,并且需要為它們分配i節(jié)點和數(shù)據(jù)塊。由于內(nèi)核在遇到一個符號鏈接時需要重新開始路徑名到i節(jié)點的轉(zhuǎn)換工作,因此會造成路徑名到i節(jié)點轉(zhuǎn)換的額外負擔(dān)。
4)設(shè)備特殊文件
在Linux類操作系統(tǒng)中,設(shè)備是可以通過特殊的文件進行訪問的。設(shè)備特殊文件(DeviceSpecialFiles)不會占用文件系統(tǒng)上的任何空間,它只是設(shè)備驅(qū)動程序的一個訪問點。
存在兩類設(shè)備特殊文件:字符設(shè)備特殊文件和塊設(shè)備特殊文件。前者允許以字符模式進行I/O操作,而后者需要通過高速緩沖功能以塊模式寫數(shù)據(jù)的方式進行操作。當(dāng)對設(shè)備特殊文件進行I/O請求操作時,就會傳遞到(虛擬的)設(shè)備驅(qū)動程序中。對特殊文件的引用是通過主設(shè)備號和次設(shè)備號進行的,主設(shè)備號確定了設(shè)備的類型,而次設(shè)備號指明了設(shè)備單元。
5)虛擬文件系統(tǒng)
Linux內(nèi)核有虛擬文件系統(tǒng)(VirtualFileSystem,VFS)層,用于系統(tǒng)調(diào)用操作文件。VFS層是一個間接層,用于處理涉及文件的系統(tǒng)調(diào)用,并調(diào)用物理文件系統(tǒng)代碼中的必要功能來進行I/O操作。該間接機制常用于Unix類操作系統(tǒng)中,以利于集成和使用幾種類型的文件系統(tǒng)。
當(dāng)處理器發(fā)出基于文件的系統(tǒng)調(diào)用時,內(nèi)核就會調(diào)用VFS函數(shù)處理與結(jié)構(gòu)無關(guān)的操作,并且把調(diào)用重新轉(zhuǎn)到與結(jié)構(gòu)相關(guān)的物理文件系統(tǒng)代碼中的函數(shù)。文件系統(tǒng)代碼使用高速緩沖功能來請求對設(shè)備進行I/O操作。
6)VFS的結(jié)構(gòu)
VFS定義了每個文件系統(tǒng)必須實現(xiàn)的函數(shù)集。該接口由一組操作集組成,涉及三類對象:文件系統(tǒng)、i節(jié)點和打開文件。
VFS知道內(nèi)核所支持的文件系統(tǒng)的類型,使用在內(nèi)核配置時定義的表來獲取這些信息。該表中的每個條目均描述了一個文件系統(tǒng)類型:含有文件系統(tǒng)類型的名稱以及在加載操作時調(diào)用函數(shù)的指針。當(dāng)需要加載一個文件系統(tǒng)時,就會調(diào)用相應(yīng)的加載函數(shù)。該函數(shù)負責(zé)從磁盤上讀取超級塊、初始化內(nèi)部變量,并且向VFS返回被加載文件系統(tǒng)的描述符。在文件系統(tǒng)已被加載以后,VFS函數(shù)就可以使用這個描述符來訪問物理文件系統(tǒng)的子程序。
VFS還使用了另外兩類描述符:i節(jié)點描述符和打開文件描述符。每個描述符含有與所使用文件相關(guān)的信息以及物理文件系統(tǒng)代碼提供的操作集。i節(jié)點描述符含有用于任何文件操作(例如create、unlink)的函數(shù)指針集,而文件描述符含有操作已被打開文件的函數(shù)的指針(例如read、write)。7.1.4常見的嵌入式文件系統(tǒng)
文件系統(tǒng)都會被燒錄在某一存儲設(shè)備上。嵌入式設(shè)備上很少使用大容量的IDE硬盤作為自己的存儲設(shè)備,嵌入式設(shè)備往往選用ROM、閃存等作為主要存儲設(shè)備。在嵌入式設(shè)備上選用哪種文件系統(tǒng)格式與閃存的特點是相關(guān)的。
Linux支持多種文件系統(tǒng),包括Ext2、Ext3、VFAT、NTFS、ISO9660、JFFS、JFFS、Romfs和NFS等,為了對各類文件系統(tǒng)進行統(tǒng)一管理,Linux引入了虛擬文件系統(tǒng),為各類文件系統(tǒng)提供一個統(tǒng)一的操作界面和應(yīng)用編程接口。
Linux下的文件系統(tǒng)結(jié)構(gòu)如圖7-4所示。圖7-4Linux下的文件系統(tǒng)結(jié)構(gòu)
Linux啟動時,第一個必須掛載的是根文件系統(tǒng);若系統(tǒng)不能從指定設(shè)備上掛載根文件系統(tǒng),則系統(tǒng)會出錯而退出啟動,之后可以自動或手動掛載其他的文件系統(tǒng)。因此,一個系統(tǒng)中可以同時存在不同的文件系統(tǒng)。
1.Ext2fs文件系統(tǒng)
Ext2fs是Linux事實上的標準文件系統(tǒng),已經(jīng)取代了擴展文件系統(tǒng)(或Extfs)。Extfs支持的文件大小最大為2?GB,支持的最大文件名稱大小為255個字符,而且不支持索引節(jié)點(包括數(shù)據(jù)修改時間標記)。Ext2fs的功能更強,優(yōu)點包括:
(1)?Ext2fs支持達4TB的內(nèi)存。
(2)?Ext2fs文件名稱最長可以到1012個字符。
(3)當(dāng)創(chuàng)建文件系統(tǒng)時,管理員可以選擇邏輯塊的大小(通常大小可選擇1024?B、2048?B和4096?B)。
(4)?Ext2fs實現(xiàn)了快速符號鏈接,不需要為此分配數(shù)據(jù)塊,并且將目標名稱直接存儲在索引節(jié)點表中。這使Ext2fs的性能有所提高,特別是在速度上。
因為Ext2fs文件系統(tǒng)的穩(wěn)定性、可靠性和健壯性,所以幾乎在所有基于Linux的系統(tǒng)(包括臺式機、服務(wù)器和工作站,甚至一些嵌入式設(shè)備)上都使用Ext2fs文件系統(tǒng)。然而,在嵌入式設(shè)備中使用Ext2fs時有一些缺點,表現(xiàn)如下:
(1)?Ext2fs是為類IDE塊設(shè)備設(shè)計的,這些設(shè)備的邏輯塊大小是512?B、1?KB等的倍數(shù)。不適合扇區(qū)大小因設(shè)備不同而不同的閃存設(shè)備。
(2)??Ext2fs文件系統(tǒng)沒有提供對基于扇區(qū)的擦除/寫入操作的良好管理。在Ext2fs中,為了在一個扇區(qū)中擦除單個字節(jié),必須將整個扇區(qū)復(fù)制到RAM,擦除后再重新寫入。
(3)考慮到閃存設(shè)備具有有限的擦除壽命(大約能進行100?000次擦除),在此之后就不能使用,所以這不是一個特別好的方法。
(4)在出現(xiàn)電源故障時,Ext2fs不是防崩潰的。
(5)??Ext2fs文件系統(tǒng)不支持損耗平衡,因此縮短了扇區(qū)/閃存的壽命。(損耗平衡確保將地址范圍的不同區(qū)域輪流用于寫和/或擦除操作以延長閃存設(shè)備的壽命。)
(6)?Ext2fs沒有特別完美的扇區(qū)管理,這使設(shè)計塊驅(qū)動程序十分困難。
由于這些原因,通常相對于Ext2fs,在嵌入式環(huán)境中使用MTD/JFFS2組合是更好的選擇。
2.基于Flash的文件系統(tǒng)
Flash(閃存)作為嵌入式系統(tǒng)的主要存儲媒介,有其自身的特性。Flash的寫入操作只能把對應(yīng)位置的1修改為0,而不能把0修改為1(擦除Flash就是把對應(yīng)存儲塊的內(nèi)容恢復(fù)為1),因此,一般情況下,向Flash寫入內(nèi)容時,需要先擦除對應(yīng)的存儲區(qū)間,這種擦除是以塊(Block)為單位進行的。
閃存主要有NOR和NAND兩種技術(shù)。Flash存儲器的擦/寫次數(shù)是有限的,NANDFlash還有特殊的硬件接口和讀/寫時序。因此,必須針對Flash的硬件特性設(shè)計符合應(yīng)用要求的文件系統(tǒng);傳統(tǒng)的文件系統(tǒng)(如Ext2等)用作Flash的文件系統(tǒng)時會有諸多弊端。在嵌入式Linux下,MTD(存儲技術(shù)設(shè)備)為底層硬件(閃存)和上層(文件系統(tǒng))之間提供統(tǒng)一的抽象接口,即Flash的文件系統(tǒng)都是基于MTD驅(qū)動層的(參見圖7-4)。使用MTD驅(qū)動程序的主要優(yōu)點在于,它是專門針對各種非易失性存儲器(以閃存為主)而設(shè)計的,因而對Flash有更好的支持、管理和基于扇區(qū)的擦除、讀/寫操作接口。
一塊Flash芯片可以被劃分為多個分區(qū),各分區(qū)可以采用不同的文件系統(tǒng);兩塊Flash芯片也可以合并為一個分區(qū)使用,采用同一文件系統(tǒng)。文件系統(tǒng)是針對于存儲器分區(qū)而言的,而非存儲芯片。
1)?JFFS2
瑞典的AxisCommunications開發(fā)了最初的JFFS,RedHat的DavidWoodhouse進行了改進,推出了第二個版本JFFS2,用于微型嵌入式設(shè)備的原始閃存芯片的實際文件系統(tǒng)。JFFS2文件系統(tǒng)是日志結(jié)構(gòu)化的,這意味著是一長列節(jié)點。每個節(jié)點包含有關(guān)文件的信息,可能是文件的名稱,也可能是一些數(shù)據(jù)。相對于Ext2fs,JFFS2因為有以下優(yōu)點而在無盤嵌入式設(shè)備中得到更多應(yīng)用:
(1)??JFFS2在扇區(qū)級別上執(zhí)行閃存擦除/寫/讀操作要比Ext2文件系統(tǒng)好。
(2)??JFFS2提供了比Ext2fs更好的崩潰/掉電安全保護。當(dāng)需要更改少量數(shù)據(jù)時,Ext2文件系統(tǒng)將整個扇區(qū)復(fù)制到內(nèi)存(DRAM)中,在內(nèi)存中合并新數(shù)據(jù),并寫回整個扇區(qū)。這意味著為了更改單個字,必須對整個扇區(qū)(64?KB)執(zhí)行讀/擦除/寫例程。這樣做的效率非常低。如果在DRAM中合并數(shù)據(jù)時,發(fā)生了電源故障或其他事故,那么將丟失整個數(shù)據(jù),這是因為在將數(shù)據(jù)讀入DRAM后就擦除了閃存扇區(qū)。JFFS2附加文件而不是重寫整個扇區(qū),而且具有崩潰/掉電安全保護這一功能。
這可能是最重要的一點:JFFS2是專門為像閃存芯片的嵌入式設(shè)備創(chuàng)建的,所以整個設(shè)計提供了更好的閃存管理。當(dāng)文件系統(tǒng)已滿或接近滿時,因為垃圾收集問題JFFS2會大大放慢運行速度。
2)?YAFFS(YetAnotherFlashFileSystem)
YAFFS/YAFFS2是專為嵌入式系統(tǒng)使用NAND型閃存而設(shè)計的日志型文件系統(tǒng)。與JFFS2相比,它減少了一些功能(例如不支持數(shù)據(jù)壓縮),所以速度更快,掛載時間很短,對內(nèi)存的占用較小。另外,它還是跨平臺的文件系統(tǒng),除了Linux和eCos,還支持WindowsCE、pSOS和ThreadX等。
YAFFS/YAFFS自帶NAND芯片驅(qū)動,并且為嵌入式系統(tǒng)提供了直接訪問文件系統(tǒng)的API,用戶可以不使用Linux中的MTD與VFS,直接對文件系統(tǒng)進行操作。當(dāng)然,YAFFS也可與MTD驅(qū)動程序配合使用。
YAFFS與YAFFS2的主要區(qū)別在于,前者僅支持小頁(512?B)NAND閃存,后者則可支持大頁(2?KB)NAND閃存。同時,YAFFS2在內(nèi)存空間占用、垃圾回收速度、讀/寫速度等方面均有大幅提升。
3)?CramFS(CompressedRAMFileSystem)
CramFS是Linux的創(chuàng)始人LinusTorvalds參與開發(fā)的一種基于MTD驅(qū)動程序只讀的壓縮文件系統(tǒng)。
CramFS文件系統(tǒng)中,每一頁(4?KB)被單獨壓縮,可以隨機頁訪問,其壓縮比高達2?:?1,為嵌入式系統(tǒng)節(jié)省大量的Flash存儲空間,使系統(tǒng)可通過更低容量的Flash存儲相同的文件,從而降低了系統(tǒng)成本。
CramFS文件系統(tǒng)以壓縮方式存儲,在運行時解壓縮,所以不支持應(yīng)用程序以XIP(eXecuteInPlace,片內(nèi)運行)方式運行,所有的應(yīng)用程序要求被拷到RAM里去運行,但這并不代表要比Ramfs需求的RAM空間大一點,因為CramFS是采用分頁壓縮的方式存放文件,在讀取文件時,不會馬上就耗用過多的內(nèi)存空間,只針對目前實際讀取的部分分配內(nèi)存,尚沒有讀取的部分不分配內(nèi)存空間,當(dāng)讀取的檔案不在內(nèi)存時,CramFS文件系統(tǒng)自動計算壓縮后的資料所存的位置,再即時解壓縮到RAM中。另外,CramFS的速度快,效率高,其只讀的特點有利于保護文件系統(tǒng)免受破壞,提高了系統(tǒng)的可靠性。
由于以上特性,CramFS在嵌入式系統(tǒng)中應(yīng)用廣泛。但是它的只讀屬性同時又是它的一大缺陷,使得用戶無法對其內(nèi)容進行擴充。
CramFS映像通常放在Flash中,但是也能放在別的文件系統(tǒng)里,使用loopback設(shè)備可以把它安裝到別的文件系統(tǒng)里。
4)?Romfs
傳統(tǒng)型的Romfs文件系統(tǒng)是一種簡單、緊湊、只讀的文件系統(tǒng),不支持動態(tài)擦/寫保存,按順序存放數(shù)據(jù),因而支持應(yīng)用程序以XIP方式運行,在系統(tǒng)運行時可節(jié)省RAM空間。mCLinux系統(tǒng)通常采用Romfs文件系統(tǒng)。
其他文件系統(tǒng):FAT/FAT32也可用于實際嵌入式系統(tǒng)的擴展存儲器(例如PDA、Smartphone、數(shù)碼相機等的SD卡),這主要是為了更好地與最流行的Windows桌面操作系統(tǒng)相兼容。
3.基于RAM的文件系統(tǒng)
1)?RAMdisk
RAMdisk是將一部分固定大小的內(nèi)存當(dāng)作分區(qū)來使用。它并非實際的文件系統(tǒng),而是一種將實際的文件系統(tǒng)裝入內(nèi)存的機制,并且可以作為根文件系統(tǒng)。將一些經(jīng)常被訪問而又不會更改的文件(如只讀的根文件系統(tǒng))通過RAMdisk放在內(nèi)存中,可以明顯地提高系統(tǒng)的性能。
在Linux的啟動階段,Initrd提供了一套機制,可以將內(nèi)核映像和根文件系統(tǒng)一起載入內(nèi)存。
2)?Ramfs/Tmpfs
Ramfs是LinuxTorvalds開發(fā)的一種基于內(nèi)存的文件系統(tǒng),工作于虛擬文件系統(tǒng)(VFS)層,不能格式化,可以創(chuàng)建多個,在創(chuàng)建時可以指定其最大能使用的內(nèi)存大小。(實際上,VFS本質(zhì)上可看做一種內(nèi)存文件系統(tǒng),它統(tǒng)一了文件在內(nèi)核中的表示方式,并對磁盤文件系統(tǒng)進行緩沖。)
如果Linux已經(jīng)將Ramfs編譯進內(nèi)核,就可以很容易地使用Ramfs。只要創(chuàng)建一個目錄,加載Ramfs到該目錄即可。
#mkdir-p/RAM1
#mount-tramfsnone/RAM1
缺省情況下,Ramfs被限制最多可使用內(nèi)存大小的一半,可以通過maxsize(以KB為單位)選項來改變。
#mkdir-p/RAM1
#mount-tramfsnone/RAM1-omaxsize=10000
以上即創(chuàng)建了一個限定了最大使用內(nèi)存大小為10?MB的RAMdisk。
Tmpfs是一個虛擬內(nèi)存文件系統(tǒng),它不同于傳統(tǒng)的用塊設(shè)備形式來實現(xiàn)的RAMdisk,也不同于針對物理內(nèi)存的Ramfs。Tmpfs可以使用物理內(nèi)存,也可以使用交換分區(qū)。在Linux內(nèi)核中,虛擬內(nèi)存資源由物理內(nèi)存(RAM)和交換分區(qū)組成,這些資源由內(nèi)核中的虛擬內(nèi)存子系統(tǒng)負責(zé)分配和管理。Tmpfs就是和虛擬內(nèi)存子系統(tǒng)“打交道”的,向虛擬內(nèi)存子系統(tǒng)請求頁存儲文件,同Linux的其他請求頁的部分一樣,不知道分配給自己的頁是在內(nèi)存中還是在交換分區(qū)中。Tmpfs同Ramfs一樣,其大小也不是固定的,而是隨著所需要的空間而動態(tài)地增減。使用Tmpfs,首先在編譯內(nèi)核時需選擇“虛擬內(nèi)存文件系統(tǒng)支持(VirtualMemoryFileSystemSupport)”,然后才能加載Tmpfs文件系統(tǒng)。
#mkdir-p/mnt/tmpfs
#mounttmpfs/mnt/tmpfs-ttmpfs
為了防止Tmpfs使用過多的內(nèi)存資源而造成系統(tǒng)的性能下降或死機,可以在加載時指定Tmpfs文件系統(tǒng)大小的最大限制。
#mounttmpfs/mnt/tmpfs-ttmpfs-osize=32m
以上創(chuàng)建的Tmpfs文件系統(tǒng)規(guī)定了其最大為32?MB。不管是使用Ramfs還是Tmpfs,必須指出的是,一旦系統(tǒng)重啟,它們中的內(nèi)容將會丟失。所以哪些內(nèi)容可以放在內(nèi)存文件系統(tǒng)中需根據(jù)系統(tǒng)的具體情況而定。
Ramfs/Tmpfs文件系統(tǒng)把所有的文件都放在RAM中,所以讀/寫操作發(fā)生在RAM中,可以用Ramfs/Tmpfs存儲一些臨時性或經(jīng)常要修改的數(shù)據(jù),例如/tmp和/var目錄,這樣既避免了對Flash存儲器的讀/寫損耗,也提高了數(shù)據(jù)讀/寫速度。
Ramfs/Tmpfs相對于傳統(tǒng)的RAMdisk的不同之處主要在于:不能格式化,文件系統(tǒng)大小可隨所含文件內(nèi)容大小變化。
Tmpfs的另一個缺點是當(dāng)系統(tǒng)重新引導(dǎo)時會丟失所有數(shù)據(jù)。
4.網(wǎng)絡(luò)文件系統(tǒng)
網(wǎng)絡(luò)文件系統(tǒng)(NetworkFileSystem,NFS)是由Sun公司開發(fā)并發(fā)展起來的一項在不同機器、不同操作系統(tǒng)之間通過網(wǎng)絡(luò)共享文件的技術(shù)。在嵌入式Linux系統(tǒng)的開發(fā)調(diào)試階段,可以利用該技術(shù)在主機上建立基于NFS的根文件系統(tǒng),掛載到嵌入式設(shè)備,從而可以很方便地修改根文件系統(tǒng)的內(nèi)容。
以上討論的都是基于存儲設(shè)備的文件系統(tǒng)(Memory-basedFileSystem),它們都可用作Linux的根文件系統(tǒng)。實際上,Linux還支持邏輯的或偽文件系統(tǒng)(LogicalorPseudoFileSystem),例如PROCFS(PROC文件系統(tǒng))用于獲取系統(tǒng)信息,而DEVFS(設(shè)備文件系統(tǒng))和SYSFS用于維護設(shè)備文件。
Linux引入了虛擬文件系統(tǒng)(VFS),為各類文件系統(tǒng)提供一個統(tǒng)一的操作界面和應(yīng)用編程接口。7.2虛擬文件系統(tǒng)
Linux的文件系統(tǒng)部分中,以下分析源代碼基于2.4.20內(nèi)核。Linux中的文件系統(tǒng)主要可分為三大塊:一是上層的文件系統(tǒng)的系統(tǒng)調(diào)用,二是虛擬文件系統(tǒng),三是掛載到VFS中的各實際文件系統(tǒng),例如Ext2、JFFS等。通過具體的代碼分析來解釋Linux內(nèi)核中VFS的內(nèi)在機制,在此過程中會涉及到上層文件系統(tǒng)調(diào)用和下層實際文件系統(tǒng)如何掛載的問題。本節(jié)內(nèi)容從一個較高的角度描述Linux下的VFS文件系統(tǒng)機制,所以在敘述中更側(cè)重于整個模塊的主脈絡(luò),而不拘泥于細節(jié)。相對來說,VFS部分的代碼比較繁瑣復(fù)雜,若要對Linux下的VFS整體運作機制有個清楚的理解,建議學(xué)習(xí)本節(jié)前,先閱讀文件系統(tǒng)的源代碼,以便建立起Linux中文件系統(tǒng)最基本的概念,至少應(yīng)熟悉super、block、dentry、inode、vfsmount等數(shù)據(jù)結(jié)構(gòu)所表示的意義。
7.2.1VFS概述
VFS是一種軟件機制,稱為Linux的文件系統(tǒng)管理者似乎更確切些。與VFS相關(guān)的數(shù)據(jù)結(jié)構(gòu)只存在于物理內(nèi)存當(dāng)中,所以在每次系統(tǒng)初始化期間,Linux都首先要在內(nèi)存當(dāng)中構(gòu)造VFS的目錄樹(在Linux的源代碼里稱之為namespace),實際上便是在內(nèi)存中建立相應(yīng)的數(shù)據(jù)結(jié)構(gòu)。VFS目錄樹在Linux的文件系統(tǒng)模塊中是很重要的概念,不要將其與實際文件系統(tǒng)目錄樹相混淆。VFS中的各目錄其主要用途是用來提供實際文件系統(tǒng)的掛載點,當(dāng)然在VFS中也會涉及到文件級的操作,此處不涉及這種情況。下文提到的目錄樹或目錄,如果不特別說明,均指VFS的目錄樹或目錄。圖7-5是一種可能的目錄樹在內(nèi)存中的映像。
圖7-5VFS目錄樹在內(nèi)存中的映像7.2.2文件系統(tǒng)的注冊
文件系統(tǒng)是指會被掛載到目錄樹中的各個實際文件系統(tǒng)。所謂實際文件系統(tǒng),即是指VFS中的實際操作最終要通過它們來完成,并不意味著一定要存在于某種特定的存儲設(shè)備上。比如在Linux下可注冊“rootfs”、“proc”、“ext2”、“sockfs”等十幾種文件系統(tǒng)。1.?dāng)?shù)據(jù)結(jié)構(gòu)
在Linux源代碼中,每種實際的文件系統(tǒng)用以下的數(shù)據(jù)結(jié)構(gòu)表示:
structfile_system_type{
constchar*name;
int
fs_flags;
structsuper_block*(*read_super)(structsuper_block*,void*,int);
structmodule*owner;
structfile_system_type*next;
structlist_headfs_supers;};
注冊過程實際上是將表示各實際文件系統(tǒng)的structfile_system_type數(shù)據(jù)結(jié)構(gòu)實例化,然后形成一個鏈表,內(nèi)核中用file_systems的全局變量來指向該鏈表的表頭。
2.注冊rootfs文件系統(tǒng)
在眾多的實際文件系統(tǒng)中,之所以單獨介紹rootfs文件系統(tǒng)的注冊過程,是因為該文件系統(tǒng)與VFS的關(guān)系密切,rootfs文件系統(tǒng)是VFS存在的基礎(chǔ)。一般文件系統(tǒng)的注冊都是通過module_init宏以及do_initcalls()函數(shù)完成的(可參考module_init宏的聲明及arch\i386\vmlinux.lds文件來理解這一過程),但是rootfs的注冊是通過init_rootfs()這一初始化函數(shù)來完成的,這意味著rootfs的注冊過程是Linux內(nèi)核初始化階段不可分割的一部分。init_rootfs()通過調(diào)用register_filesystem(&rootfs_fs_type)函數(shù)完成rootfs文件系統(tǒng)注冊,其中rootfs_fs_type定義如下:
structfile_system_typerootfs_fs_type={\name:“rootfs”,\
read_super:ramfs_read_super,\
fs_flags:FS_NOMOUNT|FS_LITTER,\
owner:THIS_MODULE,\
}
注冊之后的file_systems鏈表結(jié)構(gòu)如圖7-6所示。圖7-6file_systems鏈表結(jié)構(gòu)
7.2.3VFS目錄樹的建立
本節(jié)闡述Linux在初始化階段如何建立根節(jié)點,即“/”目錄,其中包括掛載rootfs文件系統(tǒng)到根目錄“/”的具體過程。構(gòu)造根目錄的代碼在init_mount_tree()函數(shù)(fs\namespace.c)中。
首先,init_mount_tree()函數(shù)調(diào)用do_kern_mount("rootfs",0,"rootfs",NULL)來掛載已經(jīng)注冊的rootfs文件系統(tǒng)。根據(jù)前面的說法,應(yīng)該先有掛載目錄,然后在其上掛載相應(yīng)的文件系統(tǒng),然而此時VFS并沒有建立其根目錄。這是因為這里調(diào)用的do_kern_mount()函數(shù)內(nèi)部自然會創(chuàng)建關(guān)鍵的根目錄(在Linux中,目錄對應(yīng)的數(shù)據(jù)結(jié)構(gòu)是structdentry)。在這種情況下,do_kern_mount()做的工作主要如下:
(1)調(diào)用alloc_vfsmnt()函數(shù)在內(nèi)存里申請一塊該類型的內(nèi)存空間(structvfsmount*mnt),并初始化其部分成員變量。
(2)調(diào)用get_sb_nodev()函數(shù)在內(nèi)存中分配一個超級塊結(jié)構(gòu)(structsuper_block)sb,并初始化其部分成員變量,將成員s_instances插入到rootfs文件系統(tǒng)類型結(jié)構(gòu)中fs_supers指向的雙向鏈表中。
(3)通過rootfs文件系統(tǒng)中的read_super函數(shù)指針調(diào)用ramfs_read_super()函數(shù)。當(dāng)初注冊rootfs文件系統(tǒng)時,其成員read_super指針指向了ramfs_read_super()函數(shù),參見圖7-7。圖7-7分配各種數(shù)據(jù)結(jié)構(gòu)和rootfs文件系統(tǒng)的關(guān)系
(4)ramfs_read_super()函數(shù)調(diào)用ramfs_get_inode()在內(nèi)存中分配了一個inode結(jié)構(gòu)(StructInode),并初始化其部分成員變量,其中比較重要的有i_op、i_fop和i_sb:
inode->i_op=&ramfs_dir_inode_operations;
inode->i_fop=&dcache_dir_ops;
inode->i_sb=sb;
這使得將來通過文件系統(tǒng)調(diào)用對VFS發(fā)起的文件操作等指令將被rootfs文件系統(tǒng)中相應(yīng)的函數(shù)接口所接管。
(5)?ramfs_read_super()函數(shù)在分配和初始化了inode結(jié)構(gòu)之后,調(diào)用d_alloc_root()函數(shù)為VFS的目錄樹建立起關(guān)鍵的根目錄(structdentry)dentry,并將dentry中的d_sb指針指向sb,d_inode指針指向inode。
(6)將mnt中的mnt_sb指針指向sb,mnt_root和mnt_mountpoint指針指向dentry,而mnt_parent指針則指向自身。
因此,當(dāng)do_kern_mount()函數(shù)返回時,以上分配得到的各數(shù)據(jù)結(jié)構(gòu)和rootfs文件系統(tǒng)的關(guān)系如圖7-7所示。圖中mnt、sb、inode、dentry結(jié)構(gòu)塊下方的數(shù)字表示它們在內(nèi)存里被分配的先后順序。限于篇幅的原因,各結(jié)構(gòu)中只給出了部分成員變量,可以對照源代碼根據(jù)圖中所示加以理解。
最后,init_mount_tree()函數(shù)會為系統(tǒng)最開始的進程(即init_task進程)準備它的進程數(shù)據(jù)塊中的namespace域,主要目的是將do_kern_mount()函數(shù)中建立的mnt和dentry信息記錄在init_task進程的進程數(shù)據(jù)塊中,所有以后從init_task進程fork出來的進程也都先天地繼承了這一信息,在后面用sys_mkdir在VFS中創(chuàng)建目錄的過程中,為進程建立namespace的主要代碼如下:namespace=kmalloc(sizeof(*namespace),GFP_KERNEL);
list_add(&mnt->mnt_list,&namespace->list);//mntisreturnedbydo_kern_mount()
namespace->root=mnt;
init_space=namespace;
for_each_task(p){
get_namespace(namespace);
p->namespace=namespace;
}
set_fs_pwd(current->fs,namespace->root,namespace->root->mnt_root);
set_fs_root(current->fs,namespace->root,namespace->root->mnt_root);
該段代碼的最后兩行便是將do_kern_mount()函數(shù)中建立的mnt和dentry信息記錄在當(dāng)前進程的fs結(jié)構(gòu)中。
對以上數(shù)據(jù)結(jié)構(gòu)來歷的描述,最終是要在內(nèi)存中建立VFS目錄樹。更確切地說,init_mount_tree()函數(shù)為VFS建立了根目錄“/”,而一旦有了根目錄,就可以通過系統(tǒng)調(diào)用sys_mkdir建立新的節(jié)點,所以系統(tǒng)設(shè)計者又將rootfs文件系統(tǒng)掛載到了根目錄上。關(guān)于rootfs文件系統(tǒng),如果參考一下圖7-6中的file_system_type結(jié)構(gòu),會發(fā)現(xiàn)一個成員函數(shù)指針read_super指向的是ramfs_read_super,單從這個函數(shù)名稱中的Ramfs,這個文件所涉及的文件操作都是針對內(nèi)存中的數(shù)據(jù)對象。從另一個角度而言,VFS本身就是內(nèi)存中的一個數(shù)據(jù)對象,在其上的操作僅限于內(nèi)存。在接下來的章節(jié)中,將通過具體的例子討論如何利用rootfs所提供的函數(shù)為VFS增加一個新的目錄節(jié)點。
VFS中各目錄的主要用途是為以后掛載文件系統(tǒng)提供掛載點,所以真正的文件操作還是通過掛載后的文件系統(tǒng)提供的功能接口來進行。7.2.4VFS下目錄的建立
為了更好地理解VFS,下文通過實例描述Linux如何在VFS的根目錄下建立新的目錄“/dev”。
要在VFS中建立新的目錄,首先對該目錄進行搜索,搜索的目的是找到將要建立的目錄其父目錄的相關(guān)信息。比如要建立目錄/home/ricard,那么首先必須沿目錄路徑進行逐層搜索。本例先從根目錄找起,然后在根目錄下找到home目錄,然后再往下,便是要新建的目錄名ricard,那么前面提到的要先對目錄搜索,在該例中便是要找到ricard這個新目錄的父目錄,也就是home目錄所對應(yīng)的信息。當(dāng)然,如果搜索的過程中發(fā)現(xiàn)錯誤,比如要建立目錄的父目錄并不存在,或者當(dāng)前進程并無相應(yīng)的權(quán)限等等,這種情況下系統(tǒng)必然會調(diào)用相關(guān)的過程進行處理。
Linux下用系統(tǒng)調(diào)用sys_mkdir在VFS目錄樹中增加新節(jié)點。同時,為了配合路徑搜索,引入了下面一個數(shù)據(jù)結(jié)構(gòu):
structnameidata{
structdentry*dentry;
structvfsmount*mnt;
structqstrlast;
unsignedintflags;
intlast_type;
};這個數(shù)據(jù)結(jié)構(gòu)在路徑搜索的過程中用來記錄相關(guān)信息,起著類似“路標”的作用。其中前兩項中的dentry記錄的是要建立目錄的父目錄信息,mnt成員接下來會予以解釋。后三項記錄的是所查找路徑的最后一個節(jié)點(即待建目錄或文件)信息。現(xiàn)在為建立目錄“/dev”而調(diào)用sys_mkdir("/dev",0700),其中參數(shù)0700不需關(guān)注,只是限定將要建立目錄的某種模式。sys_mkdir函數(shù)首先調(diào)用path_lookup("/dev",LOOKUP_PARENT,&nd)對路徑查找,其中nd為structnameidatand聲明的變量。在接下來的敘述中,因為函數(shù)調(diào)用關(guān)系較繁瑣,為了突出過程主線,將不再嚴格按照函數(shù)的調(diào)用關(guān)系來描述。
path_lookup發(fā)現(xiàn)“/dev”是以“/”開頭,所以從當(dāng)前進程的根目錄開始往下查找,具體代碼如下:
nd->mnt=mntget(current->fs->rootmnt);
nd->dentry=dget(current->fs->root);
在init_mount_tree()函數(shù)的后半段曾經(jīng)將新建立的VFS根目錄相關(guān)信息記錄在了init_task進程的進程數(shù)據(jù)塊中,于是nd->mnt便指向了圖7-7中的mnt變量,nd->dentry便指向了圖7-7中的dentry變量。然后調(diào)用函數(shù)path_walk接著查找,運行一次最后通過變量nd返回的信息是=“dev”,nd.last.len=3,nd.last_type=LAST_NORM,至于nd中mnt和dentry成員,此時還是前面設(shè)置的值,并無變化。這樣運行一次,只是用nd記錄了相關(guān)信息,實際的目錄建立工作并沒有真正展開,但是前面所做的工作卻為接下來建立新節(jié)點收集了必要的信息。
到此為止,真正建立新目錄節(jié)點的工作即將展開,由函數(shù)lookup_create來完成,調(diào)用這個函數(shù)時會傳入兩個參數(shù):lookup_create(&nd,1)。其中參數(shù)nd便是前面提到的變量,參數(shù)1表明要建立一個新目錄。建立新目錄節(jié)點的過程如下:新分配了一個structdentry結(jié)構(gòu)的內(nèi)存空間,用于記錄dev目錄所對應(yīng)的信息,該dentry結(jié)構(gòu)將會掛接到其父目錄中,也就是圖7-7中“/”目錄對應(yīng)的dentry結(jié)構(gòu)中,由鏈表實現(xiàn)這一關(guān)系。接下來再分配一個structinode結(jié)構(gòu)。inode中的i_sb和dentry中的d_sb分別
都指向圖7-7中的sb,在同一文件系統(tǒng)下建立新的目錄時并不需要重新分配超級塊結(jié)構(gòu),因為它們都屬于同一文件系統(tǒng),所以一個文件系統(tǒng)只對應(yīng)一個超級塊。當(dāng)調(diào)用sys_mkdir成功地在VFS的目錄樹中新建立一個目錄“/dev”之后,在圖7-7的基礎(chǔ)上,新的數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系如圖7-8所示。圖7-8中兩個矩形塊new_inode和new_dentry便是在sys_mkdir()函數(shù)中新分配的內(nèi)存結(jié)構(gòu),至于圖中的mnt、sb、dentry、inode等結(jié)構(gòu),仍為圖7-7中相應(yīng)的數(shù)據(jù)結(jié)構(gòu),其相互之間的鏈接關(guān)系不變(圖中為避免過多的鏈接曲線,忽略了一些鏈接關(guān)系,如mnt和sb、dentry之間的鏈接,可在圖7-7的基礎(chǔ)上參看圖7-8)。圖7-8在VFS樹中新建一目錄“dev”既然rootfs文件系統(tǒng)被掛載到了VFS樹上,rootfs在sys_mkdir的過程中必然會參與進來,在整個過程中,rootfs文件系統(tǒng)中的ramfs_mkdir、ramfs_lookup等函數(shù)都曾被調(diào)用過。7.2.5在VFS樹中掛載文件系統(tǒng)
在本節(jié)中,將描述在VFS的目錄樹中向其中某個目錄(安裝點(MountPoint))上掛載(Mount)一個文件系統(tǒng)的過程。
將某一設(shè)備(dev_name)上某一文件系統(tǒng)(file_system_type)安裝到VFS目錄樹上的某一安裝點(dir_name)。要解決的問題是:將對VFS目錄樹中某一目錄的操作轉(zhuǎn)化為具體安裝到其上的實際文件系統(tǒng)的對應(yīng)操作。比如說,如果將hda2上的根文件系統(tǒng)(假設(shè)文件系統(tǒng)類型為Ext2)安裝到了前一節(jié)中新建立的“/dev”目錄上(此時,“/dev”目錄就成為了安裝點),那么安裝成功之后應(yīng)達到這樣的目的,即對VFS文件系統(tǒng)的“/dev”目錄執(zhí)行l(wèi)s指令,該條指令應(yīng)能列出hda2上Ext2文件系統(tǒng)的根目錄下所有的目錄和文件。這里的關(guān)鍵是如何將對VFS樹中“/dev”的目錄操作指令轉(zhuǎn)化為安裝在其上的Ext2這一實際文件系統(tǒng)中的相應(yīng)指令。對目錄或文件的操作將最終由目錄或文件所對應(yīng)的inode結(jié)構(gòu)中的i_op和i_fop所指向的函數(shù)表中對應(yīng)的函數(shù)來執(zhí)行。
通過將對“/dev”目錄所對應(yīng)的inode中i_op和i_fop的調(diào)用轉(zhuǎn)換到hda2上根文件系統(tǒng)ext2中根目錄所對應(yīng)的inode中i_op和i_fop的操作。
初始過程由sys_mount()系統(tǒng)調(diào)用函數(shù)發(fā)起,該函數(shù)原型聲明如下:
asmlinkagelongsys_mount(char*dev_name,char*dir_name,char*type,
unsignedlongflags,void*data);
其中,參數(shù)char*type為標識將要安裝的文件系統(tǒng)類型字符串,對于Ext2文件系統(tǒng)而言,就是“ext2”。參數(shù)flags為安裝時的模式標識數(shù),和接下來的data參數(shù)一樣。為了更好地理解這一過程,下面列舉一個具體實例,將來自主硬盤第2分區(qū)(hda2)上的Ext2文件系統(tǒng)安裝到前面創(chuàng)建的“/dev”目錄中,那么對于sys_mount()函數(shù)的調(diào)用具體為
sys_mount(“hda2”,“/dev”,“ext2”,…);
該函數(shù)在將這些來自用戶內(nèi)存空間(UserSpace)的參數(shù)拷貝到內(nèi)核空間后,便調(diào)用do_mount()函數(shù)開始真正的文件系統(tǒng)安裝。
do_mount()函數(shù)首先調(diào)用path_lookup()函數(shù)得到安裝點的相關(guān)信息,如同創(chuàng)建目錄過程所述,該安裝點的信息最終記錄在structnameidata類型的變量中,為敘述方便,記該變量為nd。在本例中,當(dāng)path_lookup()函數(shù)返回時,nd中記錄的信息如下:nd.entry=new_entry;nd.mnt=mnt;?,變量如圖7-7和圖7-8中所示。
然后,do_mount()函數(shù)根據(jù)調(diào)用參數(shù)flags決定調(diào)用以下四個函數(shù)之一:do_remount()、do_loopback()、do_move_mount()、do_add_mount()。在當(dāng)前的例子中,系統(tǒng)會調(diào)用do_add_mount()函數(shù)來向VFS樹中的安裝點“/dev”安裝一個實際的文件系統(tǒng)。在do_add_mount()中,主要完成了兩件重要事情:一是獲得一個新的安裝區(qū)域塊,二是將該新的安裝區(qū)域塊加入安裝系統(tǒng)鏈表。分別調(diào)用do_kern_mount()函數(shù)和graft_tree()函數(shù)完成。
do_kern_mount()函數(shù)建立新的安裝區(qū)域塊,具體內(nèi)容在前面VFS目錄樹的建立中已經(jīng)敘述。
graft_tree()函數(shù)將do_kern_mount()函數(shù)返回的structvfsmount類型的變量加入到安裝系統(tǒng)鏈表中,同時graft_tree()將新分配的structvfsmount類型的變量加入到一個hash表中。當(dāng)do_kern_mount()函數(shù)返回時,在圖7-8的基礎(chǔ)上,新的數(shù)據(jù)結(jié)構(gòu)間的關(guān)系將如圖7-9所示。其中,圓圈區(qū)域里面的數(shù)據(jù)結(jié)構(gòu)便是安裝區(qū)域塊的內(nèi)容,其中可以稱e2_mnt為安裝區(qū)域塊的指針,始于e2.mnt的三條箭頭曲線即構(gòu)成了安裝系統(tǒng)鏈表。圖7-9安裝ext2類型根文件系統(tǒng)到“/dev”目錄上在把這些函數(shù)調(diào)用后形成的數(shù)據(jù)結(jié)構(gòu)關(guān)系理清之后,再回到本節(jié)開始提到的問題,即將ext2文件系統(tǒng)安裝到了“/dev”上之后,對該目錄上的操作如何轉(zhuǎn)化為對ext2文件系統(tǒng)相應(yīng)的操作。從圖7-9上可看到,對sys_mount()函數(shù)的調(diào)用并沒有直接改變“/dev”目錄所對應(yīng)的inode(即圖中的new_inode變量)結(jié)構(gòu)中的i_op和i_fop指針,而且“/dev”所對應(yīng)的dentry(即圖中的new_dentry變量)結(jié)構(gòu)仍然在VFS的目錄樹中,并沒有被隱藏。相應(yīng)地,來自hda2上的ext2文件系統(tǒng)的根目錄所對應(yīng)的e2_entry也不是將VFS目錄樹中的new_dentry取而代之,那么這之間的轉(zhuǎn)化如何實現(xiàn)?請注意下面的這段代碼:
while(d_mountpoint(dentry)&&__follow_down(&nd->mnt,&dentry));
這段代碼在link_path_walk()函數(shù)中被調(diào)用,link_path_
walk()最終又會被path_lookup()函數(shù)調(diào)用,如果閱讀過Linux關(guān)于文件系統(tǒng)部分的代碼,就可以了解path_lookup()函數(shù)在整個Linux繁瑣的文件系統(tǒng)代碼中屬于一個重要的基礎(chǔ)性的函數(shù)。這個函數(shù)用于解析文件路徑名,這里的文件路徑名和在應(yīng)用程序中所涉及到的概念相同,比如在Linux的應(yīng)用程序中打開或讀取一個文件/home/windfly.cs時,這里的/home/windfly.cs就是文件路徑名,path_lookup()函數(shù)的責(zé)任就是對文件路徑名進行搜索,直到找到目標文件所屬目錄對應(yīng)的dentry或者將目標直接作為一個目錄,只要記住path_lookup()會返回一個目標目錄即可。上面的代碼在總體中不很顯著,以至于初次閱讀文件系統(tǒng)的代碼時會忽略掉,但是前文所提到從VFS的操作到實際文件系統(tǒng)操作的轉(zhuǎn)化卻由它完成,對VFS中實現(xiàn)的文件系統(tǒng)的安裝十分重要。仔細剖析該段代碼:d_mountpoint
(dentry)的作用很簡單,只是返回dentry中d_mounted成員變量的值。這里的dentry仍然是在VFS目錄樹上。如果VFS目錄樹上某個目錄被安裝過一次,那么該值為1。對VFS中的目錄可進行多次安裝,后面會有例子說明。
/dev”所對應(yīng)的new_dentry中d_mounted=1,所以while循環(huán)中第一個條件滿足。在_follow_down(&nd->mnt,&dentry)代碼中,nd中的dentry成員就是圖7-9所示的new_dentry,nd中的mnt成員就是圖7-9所示的mnt,所以可以_follow_down
(&nd->mnt,&dentry)改寫成_follow_down(&mnt,?&new_
dentry)。接下來將_follow_down()函數(shù)的代碼改寫(只是去除掉一些不太相關(guān)的代碼,并且為了便于說明,在部分代碼行前加上了序號)如下:staticinlineint__follow_down(structvfsmount**mnt,structdentry**dentry)
{
structvfsmount*mounted;
[1]mounted=lookup_mnt(*mnt,*dentry);
if(mounted){
[2]*mnt=mounted;
[3]*dentry=mounted->mnt_root;
return1;
}
return0;
}代碼行[1]中的lookup_mnt()函數(shù)用于查找VFS目錄樹下某一目錄最近一次被掛載時的安裝區(qū)域塊的指針,在本例中最終會返回圖7-9中的e2_mnt。查找的原理如下:當(dāng)在安裝ext2文件系統(tǒng)到“/dev”時,在后期會調(diào)用graft_tree()函數(shù),在這個函數(shù)里會把圖7-9中的安裝區(qū)域塊指針e2_mnt掛到hash表(Linux2.4.20源代碼中稱之為mount_hashtable)中的某一項,而該項的鍵值由被安裝點所對應(yīng)的dentry(本例中new_dentry)
和
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年環(huán)保產(chǎn)業(yè)營業(yè)執(zhí)照租賃及污染治理技術(shù)合作合同3篇
- 2025版盒飯配送與外賣配送員培訓(xùn)合作合同3篇
- 2025版建筑鋼筋加工配送與施工現(xiàn)場管理合同3篇
- 2025版新能源充電設(shè)施建設(shè)投資合同
- 2024年汽車直租融資租賃合同模板及操作指南3篇
- 2024年注塑模具定制加工協(xié)議范本版
- 2024年門窗定制生產(chǎn)合同版B版
- 2024年版:人工智能語音識別技術(shù)研發(fā)合同
- 2024年獨家供應(yīng)合作協(xié)議樣例版B版
- 2025年度建筑施工現(xiàn)場安全文明施工合同范本
- 甘肅蘭州生物制品研究所筆試題庫
- 小學(xué)校門口突發(fā)問題應(yīng)急預(yù)案(5篇)
- 雙方共同招工協(xié)議書(2篇)
- 2021-2022學(xué)年第二學(xué)期《大學(xué)生職業(yè)發(fā)展與就業(yè)指導(dǎo)2》學(xué)習(xí)通超星期末考試答案章節(jié)答案2024年
- 國家開放大學(xué)電大本科《工程經(jīng)濟與管理》2023-2024期末試題及答案(試卷代號:1141)
- 醫(yī)院勞務(wù)外包服務(wù)方案(技術(shù)方案)
- 2023年肝糖原的提取鑒定與定量實驗報告
- 2012雷克薩斯lx570lx460原廠中文維修手冊
- 土方碾壓試驗施工方案1
- 主要原材料價格趨勢分析圖
- 10kV無功補償裝置安裝施工技術(shù)措施要點
評論
0/150
提交評論