日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

【转】如何实现一个文件系统

發(fā)布時間:2023/12/13 windows 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】如何实现一个文件系统 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

如何實現(xiàn)一個文件系統(tǒng)

摘要

本章目的是分析在Linux系統(tǒng)中如何實現(xiàn)新的文件系統(tǒng)。在介紹文件系統(tǒng)具體實現(xiàn)前先介紹文件系統(tǒng)的概念和作用,抽象出文件系統(tǒng)概念模型。熟悉文件系統(tǒng)的內(nèi)涵后,我們再進一步討論Linux系統(tǒng)中文件系統(tǒng)的特殊風(fēng)格和具體文件系統(tǒng)在Linux中的組成結(jié)構(gòu),逐步為讀者勾畫出Linux中文件系統(tǒng)工作的全景圖。最后在事例部分,我們將以romfs文件系統(tǒng)作實例,分析實現(xiàn)文件系統(tǒng)的普遍步驟。

什么是文件系統(tǒng)

別混淆“文件系統(tǒng)”

首先要談的概念就是什么是文件系統(tǒng),它的作用到底是什么。

文件系統(tǒng)的概念雖然許多人都認(rèn)為是再清晰不過的了,但其實我們往往在談?wù)撝谢蚨嗷蛏俚乜浯蠡蚩s小了它的實際概念(至少我時常混淆),或者說,有時借用了其它概念,有時說得又不夠全面。

比如在操作系統(tǒng)中,文件系統(tǒng)?這個術(shù)語往往既被用來描述磁盤中的物理布局,比如有時我們說磁盤中的“文件系統(tǒng)”是EXT2或說把磁盤格式化成FAT32格式的“文件系統(tǒng)”等——這時所說的“文件系統(tǒng)”是指磁盤數(shù)據(jù)的物理布局格式;另外,文件系統(tǒng)也被用來描述內(nèi)核中的邏輯文件結(jié)構(gòu),比如有時說的“文件系統(tǒng)”的接口或內(nèi)核支持Ext2等“文件系統(tǒng)”——這時所說的文件系統(tǒng)都是內(nèi)存中的數(shù)據(jù)組織結(jié)構(gòu)而并非磁盤物理布局(后面我們將稱呼它為邏輯文件系統(tǒng));還有些時候說“文件系統(tǒng)”負(fù)責(zé)管理用戶讀寫文件——這時所說的“文件系統(tǒng)”往往描述操作系統(tǒng)中的“文件管理系統(tǒng)”,也就是文件子系統(tǒng)。

雖然上面我們列舉了混用文件系統(tǒng)的概念的幾種情形,但是卻也不能說上述說法就是錯誤的,因為文件系統(tǒng)概念本身就囊括眾多概念,幾乎可以說在操作系統(tǒng)中自內(nèi)存管理、系統(tǒng)調(diào)度到I/O系統(tǒng)、設(shè)備驅(qū)動等各個部分都和文件系統(tǒng)聯(lián)系密切,有些部分和文件系統(tǒng)甚至未必能明確劃分——所以不能只知道文件系統(tǒng)是系統(tǒng)中數(shù)據(jù)的存儲結(jié)構(gòu),一定要全面認(rèn)識文件系統(tǒng)在操作系統(tǒng)中的角色,才能具備自己開發(fā)新文件系統(tǒng)的能力。

文件系統(tǒng)的體系結(jié)構(gòu)

為了澄清文件系統(tǒng)的概念,必須先來看看文件系統(tǒng)在操作系統(tǒng)中處于何種角色,分析文件系統(tǒng)概念的內(nèi)涵外延。我們先拋開Linux文件系統(tǒng)的實例,而來看看操作系統(tǒng)中文件系統(tǒng)的普遍體系結(jié)構(gòu),從而增強對文件系統(tǒng)的理論認(rèn)識。

下面以軟件組成的結(jié)構(gòu)圖[1]的方式來描述文件系統(tǒng)所涉及的內(nèi)容。

?

?

????????????????????圖?1?:?文件系統(tǒng)體系結(jié)構(gòu)層次圖

針對各層簡要分析如下:

1.首先我們來分析最低層——設(shè)備驅(qū)動層,該層負(fù)責(zé)與外設(shè)——磁盤等——通訊。文件系統(tǒng)都需要和存儲設(shè)備打交道,而系統(tǒng)操作外設(shè)時離不開驅(qū)動程序。所以內(nèi)核對文件的最后操作行為就是調(diào)用設(shè)備驅(qū)動程序完成從主存(內(nèi)存)到輔存(磁盤)的數(shù)據(jù)傳輸。

文件系統(tǒng)相關(guān)的多數(shù)設(shè)備都屬于塊設(shè)備,常見的塊設(shè)備驅(qū)動程序有磁盤驅(qū)動,光驅(qū)驅(qū)動等,之所以稱它們?yōu)閴K設(shè)備,一個原因是它們讀寫數(shù)據(jù)都是成塊進行的,但是更重要的原因是它們管理的數(shù)據(jù)能夠被隨機訪問——不需要像字符設(shè)備那樣必須順序訪問。

2.設(shè)備驅(qū)動層的上一層是物理I/O層,該層主要作為計算機外部環(huán)境和系統(tǒng)的接口,負(fù)責(zé)系統(tǒng)和磁盤之間數(shù)據(jù)塊的交換。它要知道數(shù)據(jù)塊在磁盤中地存儲位置,也要知道文件數(shù)據(jù)塊在內(nèi)存緩沖中的位置,另外,它不需要了解數(shù)據(jù)或文件的具體結(jié)構(gòu)。可以看到,這層最主要的工作是識別磁盤扇區(qū)和內(nèi)存緩沖塊[2]之間的映射關(guān)系。

3.再上層是基礎(chǔ)I/O監(jiān)督層,該層主要負(fù)責(zé)選擇文件?I/O需要的設(shè)備,調(diào)度磁盤請求等工作,另外分配I/O緩沖和磁盤空間也在該層完成。由于塊設(shè)備需要隨機訪問數(shù)據(jù),而且對速度響應(yīng)要求較高,所以操作系統(tǒng)不能像對字符設(shè)備那樣簡單、直接地發(fā)送讀寫請求,而必須對讀寫請求重新優(yōu)化排序,以能節(jié)省磁盤尋址時間,另外也必須對請求提交采取異步調(diào)度(尤其寫操作)的方式進行。總而言之,內(nèi)核必須管理塊設(shè)備請求,而這項工作正是由該層負(fù)責(zé)的。

4.倒數(shù)第二層是邏輯I/O層,該層允許用戶和應(yīng)用程序訪問記錄。它提供了通用的記錄(record)I/O操作,同時還維護基本文件數(shù)據(jù)。為了方便用戶操作和管理文件內(nèi)容,文件內(nèi)容往往被組織成記錄形式,所以操作系統(tǒng)為操作文件記錄提供了一個通用的邏輯操作層。

5.和用戶最靠近的是訪問方法層,該層提供了一個從用戶空間到文件系統(tǒng)的標(biāo)準(zhǔn)接口,不同的訪問方法反映了不同的文件結(jié)構(gòu),也反映了不同的訪問數(shù)據(jù)和處理數(shù)據(jù)的方法。這一層我們可以簡單地理解為文件系統(tǒng)給用戶提供的訪問接口——不同的文件格式(如順序存儲格式、索引存儲格式、索引順序存儲格式和哈希存儲格式等)對應(yīng)不同的文件訪問方法。該層要負(fù)責(zé)將用戶對文件結(jié)構(gòu)的操作轉(zhuǎn)化為對記錄的操作。

文件處理流程

對比上面的層次圖我們再來分析一下數(shù)據(jù)流的處理過程,加深對文件系統(tǒng)的理解。

假如用戶或應(yīng)用程序操作文件(創(chuàng)建/刪除),首先需要通過文件系統(tǒng)給用戶空間提供的訪問方法層進入文件系統(tǒng),接著使用邏輯I/O層對記錄進行給定操作,然后記錄將被轉(zhuǎn)化為文件塊,等待和磁盤交互。這里有兩點需要考慮——第一,磁盤管理(包括在磁盤空閑區(qū)分配文件和組織空閑區(qū));第二,調(diào)度塊I/O請求——這些是基礎(chǔ)I/O監(jiān)督層的工作。再下來,文件塊被物理I/O層傳遞給磁盤驅(qū)動程序,最后磁盤驅(qū)動程序才真正把數(shù)據(jù)寫入具體的扇區(qū)。至此文件操作完畢。

當(dāng)然上面介紹的層次結(jié)構(gòu)是理想情況下的理論抽象,實際文件系統(tǒng)并非一定要按照上面的層次或結(jié)構(gòu)組織,它們往往簡化或合并了某些層的功能(比如Linux文件系統(tǒng)因為所有文件都被看作字節(jié)流,所以不存在記錄,也就沒有必要實現(xiàn)邏輯I/O層,進而也不需要和記錄相關(guān)的處理)。但是大體上都需要經(jīng)過類似的處理。如果從處理對象上和系統(tǒng)獨立性上劃分,文件系統(tǒng)體系結(jié)構(gòu)可以被分為兩大部分:——文件管理部分和操作系統(tǒng)I/O部分。文件管理系統(tǒng)負(fù)責(zé)操作內(nèi)存中的文件對象,并按文件的邏輯格式將對文件對象的操作轉(zhuǎn)化成對文件塊的操作;而操作系統(tǒng)I/O部分負(fù)責(zé)內(nèi)存中的塊與物理磁盤中的數(shù)據(jù)交換。

數(shù)據(jù)表現(xiàn)形式在文件操作過程中也經(jīng)歷了幾種變化:用戶訪問文件系統(tǒng)時看到的是字節(jié)序列,而在字節(jié)序列被寫入磁盤時看到的是內(nèi)存中文件塊(在緩沖中),在最后將數(shù)據(jù)寫入磁盤扇區(qū)時看到的是磁盤數(shù)據(jù)塊[3]。

本文所說的實現(xiàn)文件系統(tǒng)主要針對最開始講到的第二種情況——內(nèi)核中的邏輯文件結(jié)構(gòu)(但其它相關(guān)的文件管理系統(tǒng)和文件系統(tǒng)磁盤存儲格式也必須了解),我們用數(shù)據(jù)處理流圖來分析一下邏輯文件系統(tǒng)的主要功能和在操作系統(tǒng)中所處的地位。

?

?

???????????????????圖?2?文件系統(tǒng)操作流程

其中文件系統(tǒng)接口與物理布局管理是邏輯文件系統(tǒng)要負(fù)責(zé)的主要功能。

l?????????文件系統(tǒng)接口為用戶提供對文件系統(tǒng)的操作,比如open、close、read、write和訪問控制等,同時也負(fù)責(zé)處理文件的邏輯結(jié)構(gòu)。

l?????????物理存儲布局管理,如同虛擬內(nèi)存地址轉(zhuǎn)化為物理內(nèi)存地址時,必須處理段頁結(jié)構(gòu)一樣,邏輯文件結(jié)構(gòu)必須轉(zhuǎn)化到物理磁盤中,所以也要處理物理分區(qū)和扇區(qū)的實際存儲位置,分配磁盤空間和內(nèi)存中的緩沖也要在這里被處理。

???????所以說要實現(xiàn)文件系統(tǒng)就必須提供上面提到的兩種功能,缺一不可。

在了解了文件系統(tǒng)的功能后,我們針對Linux操作系統(tǒng)分析具體文件系統(tǒng)如何工作,進而掌握實現(xiàn)一個文件系統(tǒng)所需要的步驟。

Linux?文件系統(tǒng)組成結(jié)構(gòu)

Linux?文件系統(tǒng)的結(jié)構(gòu)除了我們上面所提到的概念結(jié)構(gòu)外,還有兩個最主要的特點,一個是文件系統(tǒng)抽象出了一個通用文件表示層——虛擬文件系統(tǒng)或稱做VFS。另外一個重要特點就是它的文件系統(tǒng)支持動態(tài)安裝(或說掛載等),大多數(shù)文件系統(tǒng)都可以作為根文件系統(tǒng)的葉子節(jié)點被掛在到根文件目錄樹下的子目錄上。另外Linux系統(tǒng)在文件讀寫的I/O操作上也采取了一些先進技術(shù)和策略。

我們先從虛擬文件系統(tǒng)入手分析linux文件系統(tǒng)的特性,然后介紹有關(guān)文件系統(tǒng)的安裝、注冊和讀寫等概念。

虛擬文件系統(tǒng)

虛擬文件系統(tǒng)為用戶空間程序提供了文件系統(tǒng)接口。系統(tǒng)中所有文件系統(tǒng)不但依賴VFS共存,而且也依靠VFS系統(tǒng)協(xié)同工作。通過虛擬文件系統(tǒng)我們可以利用標(biāo)準(zhǔn)的UNIX文件系統(tǒng)調(diào)用對不同介質(zhì)上的不同文件系統(tǒng)進行讀寫操作[4]。

虛擬文件系統(tǒng)的目的是為了屏蔽各種各樣不同文件系統(tǒng)的相異操作形式,使得異構(gòu)的文件系統(tǒng)可以在統(tǒng)一的形式下,以標(biāo)準(zhǔn)化的方法訪問、操作。實現(xiàn)虛擬文件系統(tǒng)利用的主要思想是引入一個通用文件模型——該模型抽象出了文件系統(tǒng)的所有基本操作(該通用模型源于Unix風(fēng)格的文件系統(tǒng)),比如讀、寫操作等。同時實際文件系統(tǒng)如果希望利用虛擬文件系統(tǒng),即被虛擬文件系統(tǒng)支持,也必須將自身的諸如“打開文件”、“讀寫文件”等操作行為以及“什么是文件”,“什么是目錄”等概念“修飾”成虛擬文件系統(tǒng)所要求的(定義的)形式,這樣才能夠被虛擬文件系統(tǒng)支持和使用。

我們可以借用面向?qū)ο蟮囊恍┧枷雭砝斫馓摂M文件系統(tǒng),虛擬文件系統(tǒng)好比一個抽象類或接口,它定義(但不實現(xiàn))了文件系統(tǒng)最常見的操作行為。而具體文件系統(tǒng)好比是具體類,它們是特定文件系統(tǒng)的實例。具體文件系統(tǒng)和虛擬文件系統(tǒng)的關(guān)系類似具體類繼承抽象類或?qū)崿F(xiàn)接口。而用戶看到或操作的都是抽象類或接口,但實際行為卻發(fā)生在具體文件系統(tǒng)實例上。至于如何將對虛擬文件系統(tǒng)的操作轉(zhuǎn)化到對具體文件系統(tǒng)的實例,就要通過注冊具體文件系統(tǒng)到系統(tǒng),然后再安裝具體文件系統(tǒng)才能實現(xiàn)轉(zhuǎn)化,這點可以想象成面向?qū)ο笾械亩鄳B(tài)概念。

我們舉個實例來說明具體文件系統(tǒng)如何通過虛擬文件系統(tǒng)協(xié)同工作。

例如:假設(shè)一個用戶輸入以下shell命令:

$?cp?/hda/test1 /removable/test2

其中?/removable是MS-DOS磁盤的一個安裝點,而?/hda?是一個標(biāo)準(zhǔn)的第二擴展文件系統(tǒng)(?Ext2)的目錄。cp命令不用了解test1或test2的具體文件系統(tǒng),它所看到和操作的對象是VFS。cp首先要從ext3文件系統(tǒng)讀出test1文件,然后寫入MS-DOS文件系統(tǒng)中的test2。VFS會將找到ext3文件系統(tǒng)實例的讀方法,對test1文件進行讀取操作;然后找到MS-DOS(在Linux中稱VFAT)文件系統(tǒng)實例的寫方法,對test2文件進行寫入操作。可以看到?VFS是讀寫操作的統(tǒng)一界面,只要具體文件系統(tǒng)符合VFS所要求的接口,那么就可以毫無障礙地透明通訊了。

下圖給出Linux系統(tǒng)中VFS文件體統(tǒng)和具體文件系統(tǒng)能的層次示意圖

?

圖3?linux系統(tǒng)中VFS和具體文件系統(tǒng)關(guān)系圖

Unix風(fēng)格的文件系統(tǒng)

虛擬文件系統(tǒng)的通用模型源于Unix風(fēng)格的文件系統(tǒng),所謂Unix風(fēng)格是指Unix文件系統(tǒng)傳統(tǒng)上使用了四種和文件系統(tǒng)相關(guān)的抽象概念:文件(file)、目錄項(dentry)、索引節(jié)點(inode)和安裝點(mount point)。

1.文件——在Unix中的文件都被看做是一有序字節(jié)串,它們都有一個方便用戶或系統(tǒng)識別的名稱。另外典型的文件操作有讀、寫、創(chuàng)建和刪除等。

2.目錄項——不要和目錄概念搞混淆,在Linux中目錄被看作文件。而目錄項是文件路徑中的一部分。一個文件路徑的例子是“/home/wolfman/foo”——根目錄是/,目錄home,wolfman和文件foo都是目錄項。

3.索引節(jié)點——Unix系統(tǒng)將文件的相關(guān)信息(如訪問控制權(quán)限、大小、擁有者、創(chuàng)建時間等等信息),有時被稱作文件的元數(shù)據(jù)(也就是說,數(shù)據(jù)的數(shù)據(jù))-- 存儲到一個單獨的數(shù)據(jù)結(jié)構(gòu)中,該結(jié)構(gòu)被稱為索引節(jié)點(inode)。

4.安裝點——在Unix中,文件系統(tǒng)被安裝在一個特定的安裝點上,所有的已安裝文件系統(tǒng)都作為根文件系統(tǒng)樹中的葉子出現(xiàn)在系統(tǒng)中。

上述概念是Unix文件系統(tǒng)的邏輯數(shù)據(jù)結(jié)構(gòu),但相應(yīng)的Unix文件系統(tǒng)(Ext2等)磁盤布局也實現(xiàn)了部分上述概念,比如文件信息(文件元數(shù)據(jù))存儲在磁盤塊中的索引節(jié)點上。當(dāng)文件被載入內(nèi)存時,內(nèi)核需要使用磁盤塊中的索引節(jié)點來裝配內(nèi)存中的索引節(jié)點。類似行為還有超級塊信息等。

對于非Unix風(fēng)格文件系統(tǒng),如FAT或NTFS,要想能被VFS支持,它們的文件系統(tǒng)代碼必須提供這些概念的虛擬形式。比如,即使一個文件系統(tǒng)不支持索引節(jié)點,它也必須在內(nèi)存中裝配起索引節(jié)點結(jié)構(gòu)體——如同本身固有一樣。或者,如果一個文件系統(tǒng)將目錄看作是一種特殊對象,那么要想使用VFS,必須將目錄重新表示為文件形式。通常,這種轉(zhuǎn)換需要在使用現(xiàn)場引入一些特殊處理,使得非Unix文件系統(tǒng)能夠兼容Unix文件系統(tǒng)的使用規(guī)則和滿足VFS的需求。通過這些處理,非Unix文件系統(tǒng)便可以和VFS一同工作了,但性能上多少會受一些影響[5]。這點很重要,我們實現(xiàn)自己的文件系統(tǒng)時必須提供(模擬)Unix風(fēng)格的文件系統(tǒng)的抽象概念。

?

Linux文件系統(tǒng)中使用的對象

Linux文件系統(tǒng)的對象就是指一些數(shù)據(jù)結(jié)構(gòu)體,之所以稱它們是對象,是因為這些數(shù)據(jù)結(jié)構(gòu)體不但包含了相關(guān)屬性而且還包含了操作自身結(jié)構(gòu)的函數(shù)指針,這種將數(shù)據(jù)和方法進行封裝的思想和面向?qū)ο笾袑ο蟾拍钜恢?#xff0c;所以這里我們就稱它們是對象。

??? Linux文件系統(tǒng)使用大量對象,我們簡要分析一下和VFS相關(guān)的對象,除此之外,還有和進程相關(guān)的一些其它對象。

VFS相關(guān)對象

這里我們不展開討論每個對象,僅僅是為了內(nèi)容完整性,作簡要說明。

VFS中包含有四個主要的對象類型,它們分別是:

l?????????超級塊對象,它代表特定的已安裝文件系統(tǒng)。

l?????????索引節(jié)點對象,它代表特定文件。

l?????????目錄項對象,它代表特定的目錄項。

l?????????文件對象,它代表被進程打開的文件。

每個主要對象中都包含一個操作對象,這些操作對象描述了內(nèi)核針對主要對象可以使用的方法。最主要的幾種操作對象如下:

super_operations對象,其中包括內(nèi)核針對特定文件系統(tǒng)所能調(diào)用的方法,比如read_inode()和sync_fs()方法等。

inode_operations對象,其中包括內(nèi)核針對特定文件所能調(diào)用的方法,比如create()和link()方法等。

dentry_operations對象,其中包括內(nèi)核針對特定目錄所能調(diào)用的方法,比如d_compare()和d_delete()方法等。

file對象,其中包括進程針對已打開文件所能調(diào)用的方法,比如read()和write()方法等。

除了上述的四個主要對象外,VFS還包含了許多對象,比如每個注冊文件系統(tǒng)都是由file_system_type對象表示——描述了文件系統(tǒng)及其能力(比如ext3或XFS);另外每一個安裝點也都用vfsmount對象表示——包含了關(guān)于安裝點的信息,如位置和安裝標(biāo)志等。

其它VFS對象

系統(tǒng)上的每一進程都有自己的打開文件,根文件系統(tǒng),當(dāng)前工作目錄,安裝點等等。另外還有幾個數(shù)據(jù)結(jié)構(gòu)體將VFS層和文件的進程緊密聯(lián)系,它們分別是:file_struct?和fs_struct

file_struct結(jié)構(gòu)體由進程描述符中的files項指向。所有包含進程的信息和它的文件描述符都包含在其中。第二個和進程相關(guān)的結(jié)構(gòu)體是fs_struct。該結(jié)構(gòu)由進程描述符的fs項指向。它包含文件系統(tǒng)和進程相關(guān)的信息。每種結(jié)構(gòu)體的詳細(xì)信息不在這里說明了。

緩存對象

除了上述一些結(jié)構(gòu)外,為了縮短文件操作的響應(yīng)時間,提高系統(tǒng)性能,Linux系統(tǒng)采用了許多緩存對象,例如目錄緩存、頁面緩存和緩沖緩存(已經(jīng)歸入了頁面緩存),這里我們對緩存做簡單介紹。

頁高速緩存(cache)是?Linux內(nèi)核實現(xiàn)的一種主要磁盤緩存。其目的是減少磁盤的I/O操作,具體來講就是通過把磁盤中的數(shù)據(jù)緩存到物理內(nèi)存中去,把對磁盤的I/O操作變?yōu)閷ξ锢韮?nèi)存的I/O操作。頁高速緩存是由RAM中的物理頁組成的,緩存中每一頁都對應(yīng)著磁盤中的多個塊。每當(dāng)內(nèi)核開始執(zhí)行一個頁I/O操作時(通常是對普通文件中頁大小的塊進行磁盤操作),首先會檢查需要的數(shù)據(jù)是否在高速緩存中,如果在,那么內(nèi)核就直接使用高速緩存中的數(shù)據(jù),從而避免了訪問磁盤。

但我們知道文件系統(tǒng)只能以每次訪問數(shù)個塊的形式進行操作。內(nèi)核執(zhí)行所有磁盤操作都必須根據(jù)塊進行,一個塊包含一個或多個磁盤扇區(qū)。為此,內(nèi)核提供了一個專門結(jié)構(gòu)buffer_head來管理緩沖。緩沖頭[6]的目的是描述磁盤扇區(qū)和物理緩沖之間的映射關(guān)系和做I/O操作的容器。但是緩沖結(jié)構(gòu)并非獨立存在,而是被包含在頁高速緩存中,而且一個頁高速緩存可以包含多個緩沖。我們將在后面的文件讀寫部分看到數(shù)據(jù)是如何被從磁盤扇區(qū)讀入頁高速緩存中的緩沖中的。

???

文件系統(tǒng)的注冊和安裝

使用文件系統(tǒng)前必須對文件系統(tǒng)進行注冊和安裝,下面分別對這兩種行為做簡要介紹。

文件系統(tǒng)的注冊

VFS要想能將自己定義的接口映射到實際文件系統(tǒng)的專用方法上,必須能夠讓內(nèi)核識別實際的文件系統(tǒng),實際文件系統(tǒng)通過將代表自身屬性的文件類型對象(file_system_type)注冊(通過register_filesystem()函數(shù))到內(nèi)核,也就是掛到內(nèi)核中的文件系統(tǒng)類型鏈表上,來達到使文件系統(tǒng)能被內(nèi)核識別的目的。反過來,內(nèi)核也正是通過這條鏈表來跟蹤系統(tǒng)所支持的各種文件系統(tǒng)。

我們簡要分析一下注冊步驟:

struct?file_system_type?{

???????const?char *name;?????????????????????????????????????/*文件系統(tǒng)的名字*/

????????int?fs_flags;??????????????????????????????????????????????/*文件系統(tǒng)類型標(biāo)志*/

/*下面的函數(shù)用來從磁盤中讀取超級塊*/

???????struct?super_block?* (*read_super)??(struct?file_system_type?*,?int,

???????????????????????????????????const?char *, void *);

???????struct?file_system_type?* next;???????????????????/*鏈表中下一個文件系統(tǒng)類型*/

???????struct?list_head?fs_supers;??????????????????????????/*超級塊對象鏈表*/

};

其中最重要的一項是read_super()函數(shù),它用來從磁盤上讀取超級塊,并且當(dāng)文件系統(tǒng)被裝載時,在內(nèi)存中組裝超級塊對象。要實現(xiàn)一個文件系統(tǒng)首先需要實現(xiàn)的結(jié)構(gòu)體便是file_system_type結(jié)構(gòu)體。

注冊文件系統(tǒng)只能保證文件系統(tǒng)能被系統(tǒng)識別,但此刻文件系統(tǒng)尚不能使用,因為它還沒有被安裝到特定的安裝點上。所以在使用文件系統(tǒng)前必須將文件系統(tǒng)安裝到安裝點上。

文件系統(tǒng)被實際安裝時,將在安裝點創(chuàng)建一個vfsmount結(jié)構(gòu)體。該結(jié)構(gòu)體用來代表文件系統(tǒng)的實例——換句話說,代表一個安裝點。

vfsmount結(jié)構(gòu)被定義在<linux/mount.h>中,下面是具體結(jié)構(gòu)

―――――――――――――――――――――――――――――――――――――――

struct?vfsmount

{

???????struct?list_head?mnt_hash;????????????/*哈希表*/

???????struct?vfsmount?*mnt_parent;??????????????/*父文件系統(tǒng)*/

???????struct?dentry?*mnt_mountpoint;????/*安裝點的目錄項對象*/

???????struct?dentry?*mnt_root;?????????????????????/*該文件系統(tǒng)的根目錄項對象*/

???????struct?super_block?*mnt_sb;????????/*該文件系統(tǒng)的超級塊*/

???????struct?list_head?mnt_mounts;????????/*子文件系統(tǒng)鏈表*/

???????struct?list_head?mnt_child;????????????/*和父文件系統(tǒng)相關(guān)的子文件系統(tǒng)*/

???????atomic_t?mnt_count;????????????????????/*使用計數(shù)*/

???????int?mnt_flags;???????????????????????/*安裝標(biāo)志*/

???????char?*mnt_devname;????????????/*設(shè)備文件名字*/

???????struct?list_head?mnt_list;??????????????/*描述符鏈表*/

};

――――――――――――――――――――――――――――――――――――――

文件系統(tǒng)如果僅僅注冊,那么還不能被用戶使用。要想使用它還必須將文件系統(tǒng)安裝到特定的安裝點后才能工作。下面我們接著介紹文件系統(tǒng)的安裝[7]過程。

安裝過程

用戶在用戶空間調(diào)用mount()命令——指定安裝點、安裝的設(shè)備、安裝類型等——安裝指定的文件系統(tǒng)到指定目錄。mount()系統(tǒng)調(diào)用在內(nèi)核中的實現(xiàn)函數(shù)為sys_mount(),該函數(shù)調(diào)用的主要例程是do_mount(),它會取得安裝點的目錄項對象,然后調(diào)用do_add_mount()例程。

do_add_mount()函數(shù)主要做的是首先使用do_kern_mount()函數(shù)創(chuàng)建一個安裝點,再使用graft_tree()將安裝點作為葉子與根目錄樹掛接起來。

???整個安裝過程中最核心的函數(shù)就是do_kern_mount()了,為了創(chuàng)建一個新安裝點(vfsmount),該函數(shù)需要做以下幾件事情:

l????????檢查安裝設(shè)備的權(quán)利,只有root權(quán)限才有能力執(zhí)行該操作。

l??????????Get_fs_type()在文件鏈表中取得相應(yīng)文件系統(tǒng)類型(注冊時被填加到鏈表中)。

l?????????Alloc_vfsmnt()調(diào)用slab分配器為vfsmount結(jié)構(gòu)體分配存儲空間,并把它的地址存放在mnt局部變量中。

l?????????初始化mnt->mnt_devname域

l?????????分配新的超級塊并初始化它。do_kern_mount( )檢查file_system_type描述符中的標(biāo)志以決定如何進行如下操作:根據(jù)文件系統(tǒng)的標(biāo)志位,選擇相應(yīng)的方法讀取超級塊(比如對Ext2,romfs這類文件系統(tǒng)調(diào)用get_sb_dev();對于這種沒有實際設(shè)備的虛擬文件系統(tǒng)如?ramfs調(diào)用get_sb_nodev())——讀取超級塊最終要使用文件系統(tǒng)類型中的read_super方法。

????安裝過程做的最主要工作是創(chuàng)建安裝點對象,掛接給定文件系統(tǒng)到根文件系統(tǒng)的指定接點下,然后初始化超級快對象,從而獲得文件系統(tǒng)基本信息和相關(guān)操作方法(比如讀取系統(tǒng)中某個inode的方法)。

?

總而言之,注冊過程是告之內(nèi)核給定文件系統(tǒng)存在于系統(tǒng)內(nèi);而安裝是請求內(nèi)核對給定文件系統(tǒng)進行支持,使文件系統(tǒng)真正可用。

文件系統(tǒng)的讀寫

??? 要自己創(chuàng)建文件系統(tǒng)必須知道文件系統(tǒng)需要那些操作,各種操作的功能范圍,所以我們下面的內(nèi)容就是分析Linux文件系統(tǒng)的文件讀寫過程,從中獲得文件系統(tǒng)的基本功能函數(shù)信息和作用范圍。

打開文件

在對文件進行寫前,必須先打開文件。打開文件的目的是為了使得目標(biāo)文件能和當(dāng)前進程關(guān)聯(lián),同時需要將目標(biāo)文件的索引節(jié)點從磁盤載入內(nèi)存,并初始化。

open操作主要包含以下幾個工作要做(實際多數(shù)工作由sys_open()完成):

l??1?分配文件描述符號。

l??2?獲得新文件對象。

l??3?獲得目標(biāo)文件的目錄項對象和其索引節(jié)點對象(主要通過open_namei()函數(shù))——具體而言是通過調(diào)用索引節(jié)點對象(該索引節(jié)點或是安裝點或是當(dāng)前目錄)的lookup方法找到目錄項對應(yīng)的索引節(jié)點號ino,然后調(diào)用iget(sb,ino)從磁盤讀入相應(yīng)索引節(jié)點并在內(nèi)核中建立起相應(yīng)的索引節(jié)點(inode)對象(其實還是通過調(diào)用sb->s_op->read_inode()超級塊提供的方法),最后還要使用d_add(dentry,inode)函數(shù)將目錄項對象與inode對象連接起來。

l??4?初始化目標(biāo)文件對象的域,特別是把f_op域設(shè)置成索引節(jié)點中i_fop指向文件對象的操作表——以后對文件的所有操作將調(diào)用該表中的實際方法。

l???5?如果定義了文件操作的open方法(缺省),就調(diào)用它。

到此可以看到打開文件后,文件相關(guān)的“上下文”、索引節(jié)點、目錄對象等都已經(jīng)生成就緒,下一步就是實際的文件讀寫操作了。

文件讀寫

用戶空間通過read/write系統(tǒng)調(diào)用進入內(nèi)核執(zhí)行文件操作,read操作通過sys_read內(nèi)核函數(shù)完成相關(guān)讀操作,write通過sys_write內(nèi)核函數(shù)完成相關(guān)寫操作。簡而言之,sys_read( )??和sys_write( )幾乎執(zhí)行相同的步驟,請看下面:

l?1??調(diào)用fget(?)從fd獲取相應(yīng)文件對象file,并把引用計數(shù)器file->f_count減1。

l?2??檢查file->f_mode中的標(biāo)志是否允許請求訪問(讀或?qū)懖僮?#xff09;。

l?3?調(diào)用locks_verify_area(?)檢查對要訪問的文件部分是否有強制鎖。

l?4??調(diào)用file->f_op->read?或file->f_op->write來傳送數(shù)據(jù)。兩個函數(shù)都返回實際傳送的字節(jié)數(shù)。

l?5??調(diào)用fput( )以減少引用計數(shù)器file->f_count的值

l?6?返回實際傳送的字節(jié)數(shù)。

搞清楚大體流程了吧?但別得意,現(xiàn)在僅僅看到的是文件讀寫的皮毛。因為這里的讀寫方法僅僅是VFS提供的抽象方法,具體文件系統(tǒng)的讀寫操作可不是表面這么簡單,接下來我們試試看能否用比較簡潔的方法把從這里開始到數(shù)據(jù)被寫入磁盤的復(fù)雜過程描述清楚。

現(xiàn)在我們要進入文件系統(tǒng)最復(fù)雜的部分——實際讀寫操作了。f_op->read/f_op->write兩個方法屬于實際文件系統(tǒng)的讀寫方法,但是對于基于磁盤的文件系統(tǒng)(必須有I/O操作),比如EXT2等,所使用的實際讀寫方法都是利用Linux系統(tǒng)已經(jīng)提供的通用函數(shù)——generic_file_read/generic_file_write來完成的,這些通用函數(shù)的作用就是確定正被訪問的數(shù)據(jù)所在物理塊的位置,并激活塊設(shè)備驅(qū)動程序開始數(shù)據(jù)傳送,它們針對Unix風(fēng)格的文件系統(tǒng)都能很好地完成功能?,所以沒必要自己再實現(xiàn)專用函數(shù)了。下面來分析這些通用函數(shù)。

先說讀方法:

第一 利用給定的文件偏移量(ppos)和讀寫字節(jié)數(shù)(count)計算出數(shù)據(jù)所在頁[8]的邏輯號(index)。

第二?然后開始傳送數(shù)據(jù)頁。

第三?更新文件指針,記錄時間戳等收尾工作。

其中最復(fù)雜的是第二部,首先內(nèi)核會檢查數(shù)據(jù)是否已經(jīng)駐存在頁高速緩存(page=?find_get_page(mmaping?,index),?其中mammping為頁高速緩存對象,index為邏輯頁號),如果在高速緩存中發(fā)現(xiàn)所需數(shù)據(jù)而且數(shù)據(jù)是有效的(通過檢查一些標(biāo)志位,如,PG_uptodate),那么內(nèi)核就可以從緩存中快速返回需要的頁;否則如果頁中的數(shù)據(jù)是無效的,那么內(nèi)核將分配一個新頁面,然后將其加入到頁高速緩存中,隨即使用address_space對象的readpage方法(mapping->a_ops->readpage(file,page))激活相應(yīng)的函數(shù)以便完成磁盤到頁的I/O數(shù)據(jù)傳送。之后[9]還要調(diào)用file_read_actor( )方法把頁中的數(shù)據(jù)拷貝到用戶態(tài)緩沖區(qū),最好進行一些收尾等工作,如更新標(biāo)志等。

?到此為止,我們才要開始涉及和系統(tǒng)I/O層打交道的工作,下面我們就來分析readpage函數(shù)具體如何激活磁盤到頁的I/O傳輸。

address_space對象的readpage方法存放的是函數(shù)地址,該函數(shù)激活從物理磁盤到頁高速緩存的I/O數(shù)據(jù)傳送。對于普通文件,該函數(shù)指針指向block_read_full_page( )函數(shù)的封裝函數(shù)。例如,REISEFS文件系統(tǒng)的readpage方法指向下列函數(shù)實現(xiàn):

???int?reiserfs?_readpage(struct?file *file,?struct?page *page)

??? {

????return?block_read_full_page(page,?reiserfs?_get_block);

}

需要封裝函數(shù)是因為block_read_full_page( )函數(shù)接受的參數(shù)為待填充頁的頁描述符及有助于block_read_full_page()找到正確塊的函數(shù)get_block的地址。該函數(shù)依賴于具體文件系統(tǒng),作用是把相對于文件開始位置的塊號轉(zhuǎn)換為相對于磁盤分區(qū)中塊位置的邏輯塊號。在這里它指向reiserfs?_get_block( )函數(shù)的地址。

block_read_full_page(?)函數(shù)的目的是對頁所在的緩沖區(qū)啟動頁I/O操作,具體將要完成這幾方面工作:

n?????????調(diào)用create_empty_buffers(??)為頁中包含的所有緩沖區(qū)[10]分配異步緩沖區(qū)首部

n?????????從頁所對應(yīng)的文件偏移量(page->index域)導(dǎo)出頁中第一個塊的文件塊號

n?????????初始化緩沖區(qū)首部,最主要的工作是通過get_block函數(shù)進行磁盤尋址,找到緩沖區(qū)的邏輯塊號(相對于磁盤分區(qū)的開始而不是普通文件的開始)

n?????????對于頁中的每個緩沖區(qū)首部,對其調(diào)用submit_bh( )函數(shù),指定操作類型為READ。

l?????????接下來的工作就該交給I/O傳輸層處理了,I/O層負(fù)責(zé)磁盤訪問請求調(diào)度和管理傳輸工作。我們簡要分析submit_bt()函數(shù),該函數(shù)總體來說目的是向tq_disk任務(wù)隊列[11]提交請求,但它所做的工作頗多,下面就簡要分析該函數(shù)的行為:

u???????從b_blocknr(邏輯塊號)和b_size(塊大小)兩個域確定磁盤上第一個塊的扇區(qū)號,即b_rsector域的值

u???????調(diào)用generic_make_request()函數(shù)向低級別的驅(qū)動程序[12]發(fā)送請求,它接受的參數(shù)為緩沖區(qū)首部bh和操作類型rw。而該函數(shù)從低級驅(qū)動程描述符blk_dev[maj]中獲得設(shè)備驅(qū)動程序請求隊列的描述符,接著調(diào)用請求隊列描述符的make_request_fn方法

????make_request_fn方法是請求隊列定義的合并相臨請求、排序請求的主要執(zhí)行函數(shù)。它將首先創(chuàng)建請求(實際上就是緩沖頭和磁盤扇區(qū)的映射關(guān)系);然后檢查請求隊列是否為空:

l?????????如果請求隊列為空,則把新的請求描述符插入其中,而且還要將請求隊列描述符插入tq_disk任務(wù)隊列,隨后再調(diào)度低級驅(qū)動程序策略例程的活動。

l?????????如果請求隊列不為空,則把新的請求描述符插入其中,試圖把它與其他已經(jīng)排隊的請求進行組合(使用電梯調(diào)度算法)。

低級驅(qū)動程序的活動策略函數(shù)是request_fn方法。

策略例程通常在新請求插入到空列隊后被啟動。隨后隊列中的所有請求要依次進行處理,直到隊列為空才結(jié)束。

策略例程request_fn(定義在請求結(jié)構(gòu)中)的執(zhí)行過程如下:

u???????策略例程處理隊列中的第一個請求并設(shè)置塊設(shè)備控制器,使數(shù)據(jù)傳送完成后產(chǎn)生一個中斷。然后策略例程就終止。

u???????數(shù)據(jù)傳送完畢后塊設(shè)備控制器產(chǎn)生中斷,中斷處理程序就激活下半部分。這個下半部分的處理程序把這個請求從隊列中刪除(end_request( ))并重新執(zhí)行策略例程來處理隊列中的下一個請求。

好了,讀操作說完了,是不是覺得不知所云呀,其實上面僅僅是抽取讀操作的骨架簡要講解,具體操作還要復(fù)雜得多,下面我們將上面的流程總結(jié)一下。

????粗略地分,讀操作依次需要經(jīng)過:

l?????????用戶界面層——負(fù)責(zé)從用戶函數(shù)經(jīng)過系統(tǒng)調(diào)用進入內(nèi)核;

l?????????基本文件系統(tǒng)層——負(fù)責(zé)調(diào)用文件讀方法,從高速緩存中搜索數(shù)據(jù)頁,返回給用戶。

l?????????I/O調(diào)度層——負(fù)責(zé)對請求排隊,從而提高吞吐量。

l?????????I/O傳輸層——利用任務(wù)隊列異步操作設(shè)備控制器完成數(shù)據(jù)傳輸。

請看下圖4給出的邏輯流程。

?

?圖?4?讀操作流

?

?

寫操作和讀操作大體相同,不同之處主要在于寫頁面高速緩存時,稍微麻煩一些,因為寫操作不象讀操作那樣必須和用戶空間同步[13]執(zhí)行,所以用戶寫操作更新了數(shù)據(jù)內(nèi)容后往往先存儲在頁高速緩存中,然后等待頁回寫后臺例程bdflush和kupdate[14]等來完成寫入磁盤的工作。當(dāng)然寫入請求處理還是要通過上面提到的submit_bh函數(shù)[15]進行I/O處理的。下面簡要介紹寫過程:

page?= __grab_cache_page(mapping,index,&cached_page,&lru_pvec);

status?=?a_ops->prepare_write(file,page,offset,offset+bytes);

page_fault?=?filemap_copy_from_user(page,offset,buf,bytes);

status?=?a_ops->commit_write(file,page,offset,offset+bytes);

首先,在頁高速緩存中搜索需要的頁,如果需要的頁不在高速緩存中,那么內(nèi)核在高速緩存中新分配一空閑項;下一步,prepare_write()方法被調(diào)用,為頁分配異步緩沖區(qū)首部;接著數(shù)據(jù)被從用戶空間拷貝到了內(nèi)核緩沖;最后通過commit_write()函數(shù)將對應(yīng)的基礎(chǔ)緩沖區(qū)標(biāo)記為臟,以便隨后它們被頁回寫例程寫回到磁盤。

好累呀,到此總算把文件讀寫過程順了一遍,大家明白了上述概念后,我們進入最后一部分:Romfs事例分析。

實例

文件系統(tǒng)實在是個龐雜的“怪物”,我很難編寫一個恰當(dāng)?shù)睦觼硌菔疚募到y(tǒng)的實現(xiàn)。開始我想寫一個純虛文件系統(tǒng),但考慮到它幾乎沒有實用價值,而且更重要的是虛文件系統(tǒng)不涉及I/O操作,缺少現(xiàn)實文件系統(tǒng)中至關(guān)重要的部分,所以放棄了;后來想寫一個實際文件系統(tǒng),但是那樣工程量太大,而且也不容易讓大家簡明扼要地理解文件系統(tǒng)的實現(xiàn),所以也放棄了。最后我發(fā)現(xiàn)內(nèi)核中提供的romfs文件系統(tǒng)是個非常理想的實例,它既有實際應(yīng)用,結(jié)構(gòu)也清晰明了,所以我們以romfs為實例分析文件系統(tǒng)的實現(xiàn)。

Linux文件系統(tǒng)實現(xiàn)要素

編寫新文件系統(tǒng)需要一些基本對象[16]。具體而言,創(chuàng)建文件系統(tǒng)需要建立“一個結(jié)構(gòu)四個操作”:

n?????????文件系統(tǒng)類型結(jié)構(gòu)(file_system_type)、

n?????????超級塊操作表(super_operations)、

n?????????索引節(jié)點操作表(inode_operations)、

n?????????頁高速緩存(address_space_operations)、

n?????????文件操作表(file_operations)。

對上述幾種結(jié)構(gòu)的處理貫穿了文件系統(tǒng)的主要操作過程,理清晰這幾種結(jié)構(gòu)之間的關(guān)系是編寫文件系統(tǒng)的基礎(chǔ),下來我們具體分析這幾個結(jié)構(gòu)和文件系統(tǒng)實現(xiàn)的要點。

?

?

?

你必須首先建立一個文件系統(tǒng)類型結(jié)構(gòu)來“描述”文件系統(tǒng),它含有文件系統(tǒng)的名稱、類型標(biāo)志以及get_sb等操作。當(dāng)你安裝文件系統(tǒng)時(mount)時,系統(tǒng)會解析“文件系統(tǒng)類型結(jié)構(gòu)”,然后使用get_sb函數(shù)來建立超級節(jié)點“sb”,注意,對于基于塊的文件系統(tǒng),如ext2、romfs等,需要從文件系統(tǒng)的宿主設(shè)備讀入超級塊以便在內(nèi)存中建立對應(yīng)的超級節(jié)點,如果是虛文件系統(tǒng)的話,則不是讀取宿主設(shè)備的信息(因為它沒有宿主設(shè)備),而是在現(xiàn)場創(chuàng)建一個超級節(jié)點,這項任務(wù)由get_sb完成。

超級節(jié)點可謂是一切文件操作的鼻祖,因為超級塊是我們尋找索引節(jié)點——索引節(jié)點對象包含了內(nèi)核在操作文件或目錄時需要的全部信息——的唯一源頭,我們操作文件必然需要獲得其對應(yīng)的索引節(jié)點(這點和建立超級節(jié)點一樣或從宿主設(shè)備讀取或現(xiàn)場建立),而獲取索引節(jié)點是通過超級塊操作表提供的read_node函數(shù)完成的,同樣操作索引節(jié)點的低層次任務(wù),如創(chuàng)建一個索引節(jié)點、釋放一個索引節(jié)點,也都是通過超級塊操作表提供的有關(guān)函數(shù)完成的。所以超級塊操作表是我們第二個需要創(chuàng)建的數(shù)據(jù)類型。

除了派生或釋放索引節(jié)點等操作是由超級塊操作表中的函數(shù)完成外,索引節(jié)點還需要許多自操作函數(shù),比如lookup搜索索引節(jié)點,為建立符號連接等,這些函數(shù)都包含在索引節(jié)點操作表中,因此我們下一個需要創(chuàng)建的數(shù)據(jù)類型就是索引節(jié)點操作表。

為了提高文件系統(tǒng)的讀寫效率,Linux內(nèi)核設(shè)計了I/O緩存機制。所有的數(shù)據(jù)無論出入都會經(jīng)過系統(tǒng)管理的高速緩存——對于非基于塊的文件系統(tǒng)則可跳過該機制。出于操作數(shù)據(jù)的目的,頁高速緩存同樣提供了一個函數(shù)操作表,其中包含有readpage()、writepage()等函數(shù),負(fù)責(zé)操作高速緩存中的頁讀寫。

?????文件系統(tǒng)最終和用戶交互還需要實現(xiàn)文件操作表,該表中包含有關(guān)用戶讀寫文件、打開、關(guān)閉、映射等用戶接口。

?????對于基于塊的文件系統(tǒng)實現(xiàn)的一般方式而言,都離不開以上5種數(shù)據(jù)結(jié)構(gòu)。但根據(jù)文件系統(tǒng)的特點(如有的文件系統(tǒng)只可讀、有的沒有目錄),并非要實現(xiàn)操作表中的全部函數(shù),換句話說,你只需要實現(xiàn)部分函數(shù),而且系統(tǒng)已經(jīng)存在很多函數(shù)現(xiàn)成的通用方法,因此留給你做的事情其實不多。

Romfs文件系統(tǒng)是什么

Romfs是基于塊的只讀文件系統(tǒng),它使用塊(或扇區(qū))訪問存儲設(shè)備驅(qū)動(比如磁盤,CD,ROM盤)。由于它小型、輕量,所以常常用在嵌入系統(tǒng)和系統(tǒng)引導(dǎo)時。

Romfs是種很簡單的文件系統(tǒng),它的文件布局和Ext2等文件系統(tǒng)相比要簡單得多。它比ext2文件系統(tǒng)要求更少的空間。空間的節(jié)約來自于兩個方面,首先內(nèi)核支持romfs文件系統(tǒng)比支持ext2文件系統(tǒng)需要更少的代碼,其次romfs文件系統(tǒng)相對簡單,在建立文件系統(tǒng)超級塊(superblock)需要更少的存儲空間。Romfs文件系統(tǒng)不支持動態(tài)擦寫保存,對于系統(tǒng)需要動態(tài)保存的數(shù)據(jù)采用虛擬ram盤的方法進行處理(ram盤將采用ext2文件系統(tǒng))。

下面我們來分析一下它的實現(xiàn)方法,為讀者勾勒出編寫新文件系統(tǒng)的思路和步驟。希望能為大家自己設(shè)計文件系統(tǒng)起到拋磚引玉的效果。

Romfs文件系統(tǒng)布局與文件結(jié)構(gòu)

文件系統(tǒng)簡單理解就是數(shù)據(jù)的分層存儲結(jié)構(gòu),數(shù)據(jù)由文件組織,而文件又由文件系統(tǒng)安排存儲形式,所以首先必須設(shè)計信息和文件組織形式——也就是這里所說的文件布局。

??????

在Linux內(nèi)核源代碼中的Document/fs/romfs中介紹了romfs文件系統(tǒng)的布局和文件結(jié)構(gòu)。下面我們簡要說明一下它們。

?

?

??????????????????????????????????????????圖?5?romfs文件系統(tǒng)布局

上圖是romfs布局圖,可以看到文件系統(tǒng)中每部分信息都是16位對的,也就是說存儲的偏移量最后4位必須為0,這樣做是為了提高訪問速度。如果信息不足時,需要填充0以保證所有信息的開始位置都為16為對齊[17]。

???文件系統(tǒng)的開始8個字節(jié)存儲文件系統(tǒng)的ASCII形式的名稱,比如“romfs”;接著4個字節(jié)記錄文件大小;然后的4個字節(jié)存儲的是文件系統(tǒng)開始處512字節(jié)的檢驗和;接下來是卷名;最后是第一個文件的文件頭,從這里開始依次存儲的信息就是文件本身了。

??Romfs的文件結(jié)構(gòu)也非常簡單,我們看下圖

?

?

?

具體需要實現(xiàn)的對象

???Romfs文件系統(tǒng)定義針對文件系統(tǒng)布局和文件結(jié)構(gòu)定義了一個磁盤超級塊結(jié)構(gòu)和磁盤inode(對應(yīng)于文件)結(jié)構(gòu):

struct romfs_super_block { ??? ?????????__u32 word0; ??????? ?????__u32 word1; ??????? ?????__u32 size; ?????????????__u32 checksum; char name[0];????????? ?}; ? struct romfs_inode { ????????? __u32 next;?????????? ?????????__u32 spec; ????????? __u32 size; ????????? __u32 checksum; ????????? char name[0]; ? };

上述兩種結(jié)構(gòu)分別描述了文件系統(tǒng)結(jié)構(gòu)與文件結(jié)構(gòu),它們將在內(nèi)核裝配超級塊對象和索引節(jié)點對象時被使用。

???Romfs文件系統(tǒng)首先要定義的對象是文件系統(tǒng)類型romfs_fs_type。定義該對象同時還要定義讀取超級塊的函數(shù)romfs_read_super。

ramfs_read_super()作用是從磁盤讀取磁盤超級塊給超級塊對象,具體行為如下

1 裝配超級塊。

??1.1 初始化超級塊對象某些域。

1.2 從設(shè)備中讀取磁盤第0塊到內(nèi)存到內(nèi)存?bread(dev,0,ROMBSIZE),其中dev是文件系統(tǒng)安裝時指定的設(shè)備,0指設(shè)備的首塊,也就是磁盤超級塊,ROMBSIZE是讀取的大小。

??1.3 檢驗磁盤超級塊中的校驗和

??1.4 繼續(xù)初始化超級塊對象某些域

2 給超級塊對象的操作表賦值(s->s_op = &romfs_ops)

3 為根目錄分配目錄項 s->s_root = d_alloc_root(iget(s,sz), sz為文件系統(tǒng)開始偏移。

???? 超級塊操作表中romfs文件系統(tǒng)實現(xiàn)了兩個函數(shù) static struct super_operations romfs_ops = { ??????? read_inode:???? romfs_read_inode, ??????? statfs:???????? romfs_statfs, };

第一個函數(shù)read_inode(inode)是用磁盤上的數(shù)據(jù)填充參數(shù)指定的索引節(jié)點對象的域;索引節(jié)點對象的i_ino域標(biāo)識從磁盤上要讀取的具體文件系統(tǒng)的索引節(jié)點。

1?根據(jù)inode參數(shù)尋找對應(yīng)的索引節(jié)點。

2?初始化索引節(jié)點的某些域

3?根據(jù)文件的訪問權(quán)限(類別)設(shè)置索引節(jié)點的相應(yīng)操作表

? 3.1 如果是目錄文件,則將索引節(jié)點表設(shè)為i->i_op = &romfs_dir_inode_operations;文件操作表設(shè)置為->i_fop = &romfs_dir_operations; 如果索引節(jié)點對應(yīng)目錄的話,那么需要的操作僅僅會是lookup操作(因為romfs是個功能很有限的文件系統(tǒng));對于文件操作表中的兩個方法一個為read,另一個為readdir。前者利用通用函數(shù)generic_read_dir,返回用戶錯誤消息。后者是針對readdir/getdents等系統(tǒng)調(diào)用的實現(xiàn)返回目錄中的文件的函數(shù) ? 3.2 如果是常規(guī)文件,則將文件操作表設(shè)置為i->i_fop = &generic_ro_fops; 將頁高訴緩存表設(shè)置為i->i_data.a_ops = &romfs_aops;由于romfs是只讀文件系統(tǒng),它在對正規(guī)文件操作時不需要索引節(jié)點操作,如mknod,link等,因此不用給出索引節(jié)點操作表。 對常規(guī)文件的操作也只需要使用內(nèi)核提供的通用函數(shù)表struct generic_ro_fops ,它包含基本的三種常規(guī)文件操作: llseek:???????? generic_file_llseek, ????read:?????????? generic_file_read, mmap:?????????? generic_file_mmap, ?? 利用這幾種通用函數(shù),完全能夠滿足romfs文件系統(tǒng)的的文件操作要求,具體函數(shù)請自己閱讀源代碼。 回憶前面我們提到過的頁高速緩存,顯然常規(guī)文件訪問需要經(jīng)過它,因此有必要實現(xiàn)頁高訴緩存操作。因為只需要讀文件,所以只用實現(xiàn)romfs_readpage函數(shù),這里readpage函數(shù)使用輔助函數(shù)romfs_copyfrom完成將數(shù)據(jù)從設(shè)備讀入頁高速緩存,該函數(shù)根據(jù)文件格式從設(shè)備讀取需要的數(shù)據(jù)。設(shè)備讀取操作需要使用bread塊I/O例程,它的作用是從設(shè)備讀取指定的塊[18]。 ? 3.3 如果是連接文件,則將索引節(jié)點操作表設(shè)置為:i->i_op=&page_symlink_inode_operations; 將頁高速緩存操作表設(shè)置為: i->i_data.a_ops = &romfs_aops; 符號連接文件需要使用通用符號連接操作page_symlink_inode_operations實現(xiàn),同時也需要使用頁高速緩存方法。 ??3.4 如果是套接字或管道,則進行特殊文件初始化操作init_special_inode(i, ino, nextfh); 到此,我們已經(jīng)遍歷了romfs文件系統(tǒng)使用的幾種對象結(jié)構(gòu):romfs_super_block、romfs_inode、romfs_fs_type、super_operations romfs_ops、address_space_operations romfs_aops 、file_operations romfs_dir_operations、inode_operations romfs_dir_inode_operations 。實現(xiàn)上述對象是設(shè)計一個新文件系統(tǒng)的最低要求。 ?

最后要說明的是,為了使得romfs文件系統(tǒng)作為模塊掛載,需要實現(xiàn)static?int?__init?init_romfs_fs(void)

{

????return?register_filesystem(&romfs_fs_type);

}

static?void __exit?exit_romfs_fs(void)

{

????unregister_filesystem(&romfs_fs_type);

}

兩個在安裝romfs文件系統(tǒng)模塊時使用的例程。

安裝和卸載

module_init(init_romfs_fs)

module_exit(exit_romfs_fs)

????到此,romfs文件系統(tǒng)的關(guān)鍵結(jié)構(gòu)都已介紹完畢,至于細(xì)節(jié)還是需要讀者仔細(xì)推敲。Romfs是最簡單的基于塊的只讀文件系統(tǒng),而且沒有訪問控制等功能。所以很多訪問權(quán)限以及寫操作相關(guān)的方法都不必去實現(xiàn)。

??????使用romfs首先需要genromfs來制作romfs文件系統(tǒng)鏡像(類似于使用mke2fs格式化文件系統(tǒng)),然后安裝文件系統(tǒng)鏡像?mount –t?romfs?********。?Romfs可以在編譯內(nèi)核時制定編譯成模塊或編入內(nèi)核,如果是模塊則需要首先加載它。

?

??? 小結(jié):實現(xiàn)文件系統(tǒng)必須向上要清楚文件系統(tǒng)和系統(tǒng)調(diào)用的關(guān)系,向下要了解文件系統(tǒng)和I/O調(diào)度、設(shè)備驅(qū)動等的聯(lián)系。另外還必須了解關(guān)于緩存、進程、磁盤格式等概念。在這里并沒有對這些問題進行深入分析,僅僅提供給大家一個文件系統(tǒng)全景圖,希望能對自己設(shè)計文件系統(tǒng)有所幫助。文件系統(tǒng)內(nèi)容龐大、復(fù)雜,許多問題我也不確定,有文件系統(tǒng)經(jīng)驗的朋友希望能夠廣泛交流。



[1]?請參見??OPERATION SYSTEMS INTERNALS AND DESIGN PRINCIPLES?一書第12章

[2]?扇區(qū)是磁盤的最小尋址單元,而文件塊是內(nèi)核操作文件的最小單位,一個塊可以包含一個或數(shù)個扇區(qū)。這些磁盤塊被讀入內(nèi)存后即刻被存入緩沖中,同樣,文件塊被寫出也要通過緩沖。

[3]?如果文件按記錄形式組織,那么數(shù)據(jù)在成為文件塊前,還要經(jīng)過記錄形式的階段。

[4]?摘自?Linux?內(nèi)核開發(fā)?中第?11?章中文件系統(tǒng)抽象層一節(jié)

[5]?請看Linux?內(nèi)核開發(fā)?一書第11章

[6]?在2.6內(nèi)核以后,緩沖頭的作用并不象以前那么重要了。因為2.6中緩沖頭僅僅作為內(nèi)核中的I/O操作單元,而在2.6以前緩沖頭不但是磁盤塊到物理內(nèi)存的映射,而且還是所有塊I/O操作的容器。

[7]?這里安裝的文件系統(tǒng)屬于非根文件系統(tǒng)的安裝方法。根文件系統(tǒng)安裝方法有所區(qū)別,請查看相關(guān)資料。

[8]?無論讀文件或?qū)懳募?#xff0c;文件中的數(shù)據(jù)都是必須經(jīng)過內(nèi)存中的頁高速緩存做中間存儲才能夠被使用。高速緩存由一個叫做address_space的特殊數(shù)據(jù)結(jié)構(gòu)表示,其中含有對頁高速緩存宿主(address_space->host)的操作表。

[9]?這期間要要處理一些預(yù)讀,以此提高未來訪問的速度。

[10]??緩沖與相應(yīng)的塊一一對應(yīng),它的作用相當(dāng)于磁盤塊在內(nèi)存中的表示。

[11]??tq_disk是專門負(fù)責(zé)磁盤請求的任務(wù)隊列,任務(wù)隊列是用來推后異步執(zhí)行的一種機制。2.6內(nèi)核中已經(jīng)用工作隊列代替了任務(wù)隊列。

[12]??塊設(shè)備驅(qū)動程序可以劃分為兩部分:低級驅(qū)動程序(blk_dev_struct)和高級設(shè)備驅(qū)動(block_device)。低級設(shè)備驅(qū)動程序作用是記錄每個高級驅(qū)動程序送來的請求組成的隊列。

[13]?文件讀取操作必須同步進行,在讀取的數(shù)據(jù)返回前,工作無法繼續(xù)進行。而且如果結(jié)果在30秒內(nèi)回不來,則用戶必將無法忍受,所以讀操作執(zhí)行緊迫。而對于寫操作,則可以異步執(zhí)行,因為寫入操作一般不會影響下一步的執(zhí)行,所以緊迫性也低。

[14]?bdflush和kupdate?分別是當(dāng)空閑內(nèi)存過低時釋放臟頁和當(dāng)臟緩沖區(qū)在內(nèi)存中存在時間過長時刷新磁盤的。而在2.6內(nèi)核中,這兩個函數(shù)的功能已經(jīng)被pdflush統(tǒng)一完成。

[15]?實際上是從block_read_full_page( )函數(shù)中調(diào)用submit_bh()函數(shù)的。

[16]?對象指的是內(nèi)存中的結(jié)構(gòu)體實例,而不是物理上的存儲結(jié)構(gòu)。

[17]?創(chuàng)建romfs文件系統(tǒng)可使用genromfs格式化工具。

[18]?索引節(jié)點結(jié)構(gòu)描述了從物理塊(塊設(shè)備上的存取單位,是每次I/O操作最小傳輸?shù)臄?shù)據(jù)大小)到邏輯塊(文件實際操作基本單元)的映射關(guān)系。

原文鏈接:http://www.kerneltravel.net/journal/vii/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F.htm

轉(zhuǎn)載于:https://www.cnblogs.com/earendil/p/5009102.html

總結(jié)

以上是生活随笔為你收集整理的【转】如何实现一个文件系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。