原文地址:https://www.usna.edu/Users/cs/aviv/classes/ic221/s16/lec/21/lec.html
1. 回顧:什么是文件系統(tǒng)霸旗?
回想一下签舞,文件系統(tǒng)是將文件組織到目錄和文件夾中秕脓。有許多不同類型的文件系統(tǒng)和許多不同的實(shí)現(xiàn),它們與包含數(shù)據(jù)的存儲(chǔ)設(shè)備緊密相連儒搭,例如吠架,硬盤、U盤搂鲫、CDrom傍药,以及操作系統(tǒng),例如Mac魂仍、Linux或Windows拐辽。
文件系統(tǒng)的目的是維護(hù)和組織二級(jí)存儲(chǔ)。與不穩(wěn)定且不會(huì)在計(jì)算機(jī)重新啟動(dòng)時(shí)持續(xù)存在的RAM不同擦酌,二級(jí)存儲(chǔ)被設(shè)計(jì)為永久存儲(chǔ)數(shù)據(jù)俱诸。文件系統(tǒng)為操作系統(tǒng)維護(hù)的數(shù)據(jù)布局提供了方便的表示。有各種各樣的文件系統(tǒng)實(shí)現(xiàn)仑氛,它們描述了組織維護(hù)文件系統(tǒng)所必需的元數(shù)據(jù)的不同方法乙埃。例如闸英,Windows上的標(biāo)準(zhǔn)文件系統(tǒng)類型稱為NTFS,它代表Windows NT文件系統(tǒng)介袜,而Linux/Unix系統(tǒng)的標(biāo)準(zhǔn)文件系統(tǒng)則根據(jù)版本的不同而被稱為ext3或ext4甫何。
OS有一個(gè)根文件系統(tǒng),其中存儲(chǔ)主數(shù)據(jù)和系統(tǒng)文件遇伞。在Unix系統(tǒng)上辙喂,這是基文件系統(tǒng),由根上的單個(gè)/表示鸠珠。在Windows上巍耗,這通常稱為C:\drive。OS還可以掛載(mount)其他文件系統(tǒng)渐排,例如插入U(xiǎn)SB驅(qū)動(dòng)器或插入CD-ROM炬太,用戶可以通過文件探索訪問這些其他文件系統(tǒng),這些文件系統(tǒng)可以使用不同的布局和數(shù)據(jù)組織驯耻,例如USB驅(qū)動(dòng)器上的FAT 32或光盤上的ISO 9660亲族。
但是,從系統(tǒng)的編程角度來看可缚,我們編寫的程序?qū)τ谖募到y(tǒng)的底層實(shí)現(xiàn)是不可知的霎迫;我們用open()打開一個(gè)文件,用read()從文件中讀取數(shù)據(jù)帘靡,用write()向文件寫入數(shù)據(jù)知给。實(shí)現(xiàn)的細(xì)節(jié)是完全透明的。OS是如何維持這種幻覺的描姚?這就是下一課的主題-我們將探索文件系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié)涩赢,支持我們到目前為止編寫的程序。
2. 內(nèi)核(Kernel)數(shù)據(jù)結(jié)構(gòu)
要理解文件系統(tǒng)轰胁,您必須首先考慮內(nèi)核是如何組織和維護(hù)信息的谒主。內(nèi)核做了大量的工作朝扼;它需要知道哪個(gè)進(jìn)程正在運(yùn)行赃阀,它們的內(nèi)存布局是什么,進(jìn)程打開了什么文件擎颖,等等榛斯。為了支持這個(gè),內(nèi)核管理著三個(gè)重要的表/數(shù)據(jù)結(jié)構(gòu)搂捧,通過這三個(gè)表來管理進(jìn)程的打開文件驮俗,這三個(gè)表示:進(jìn)程表,文件表允跑,和v-node/i-node信息表王凑。
下面我們將逐一討論搪柑。
2.1 進(jìn)程表
第一個(gè)數(shù)據(jù)結(jié)構(gòu)是進(jìn)程表,它存儲(chǔ)所有當(dāng)前正在運(yùn)行的進(jìn)程的信息索烹。這包括有關(guān)進(jìn)程的內(nèi)存布局工碾、當(dāng)前執(zhí)行點(diǎn)等的信息。還包括打開的文件描述符百姓。
As we know, all process start with with three standard files descriptors, 0, 1, 2, and these numbers and other file descriptors are indexes and stored in the open file table for that process's entry in the process table. Every time a new file is open, an new row in the open file table is added, indexed at the value of the file descriptor, e.g., 3 or 4 or 5 or etc.
打開文件表中的每一行都有兩個(gè)值渊额。第一個(gè)是描述文件的處理的標(biāo)志,例如打開或關(guān)閉垒拢,或者當(dāng)文件關(guān)閉時(shí)應(yīng)該對(duì)其采取某些操作旬迹。第二個(gè)值是對(duì)文件表?xiàng)l目的引用,文件表是一個(gè)全局列表求类,所有進(jìn)程共享奔垦,包含了當(dāng)前所有打開的文件。
有關(guān)進(jìn)程表?xiàng)l目的一個(gè)有趣的的事情是尸疆,每當(dāng)進(jìn)程通過fork創(chuàng)建子進(jìn)程時(shí)宴倍,整個(gè)進(jìn)程表?xiàng)l目都會(huì)被復(fù)制,其中包括打開的文件條目及其文件指針仓技。這是兩個(gè)進(jìn)程(父進(jìn)程和子進(jìn)程)能夠共享打開的文件鸵贬。我們在前面看到了這方面的例子,我們將在后面的課中再看一遍脖捻。
2.2 文件表
每當(dāng)打開一個(gè)新文件時(shí)阔逼,系統(tǒng)范圍內(nèi)就會(huì)在全局文件表中創(chuàng)建一個(gè)新條目。這些條目在所有進(jìn)程之間共享地沮,例如嗜浮,當(dāng)一個(gè)文件被兩個(gè)不同的進(jìn)程打開時(shí),它們可能具有相同的文件描述符號(hào)摩疑,例如3危融,但是每個(gè)文件描述符將引用文件描述符表中的不同條目。
每個(gè)文件表?xiàng)l目都包含有關(guān)當(dāng)前文件的信息雷袋。最重要的是文件的狀態(tài)吉殃,如文件讀或?qū)憼顟B(tài)和其他狀態(tài)信息。此外楷怒,文件表?xiàng)l目維護(hù)一個(gè)偏移量蛋勺,該偏移量描述從文件中讀取(或?qū)懭?了多少字節(jié),指示下一次從何處讀取/寫入鸠删。例如抱完,當(dāng)文件最初打開以供讀取時(shí),偏移量為0刃泡,因?yàn)闆]有讀取任何內(nèi)容巧娱。讀取10字節(jié)后碉怔,偏移量已向前移動(dòng)到10字節(jié),因?yàn)閺奈募凶x取了10個(gè)字節(jié)禁添。這是允許程序按順序讀取文件的機(jī)制眨层。稍后,我們將看到如何操作這個(gè)偏移量并從文件的不同部分讀取上荡。
表中的最后一個(gè)條目是一個(gè)v-node指針趴樱,它是對(duì)虛擬節(jié)點(diǎn)(v-node)和索引節(jié)點(diǎn)(i-node)的引用。這兩個(gè)節(jié)點(diǎn)包含有關(guān)如何讀取文件的信息酪捡。
2.3 V-node和I-node表
v-nodes和i-nodes是對(duì)文件的文件系統(tǒng)配置和底層存儲(chǔ)機(jī)制的引用叁征;它將軟件與硬件連接起來。例如逛薇,在某個(gè)時(shí)候捺疼,打開的文件將需要接觸磁盤,但我們知道使用不同的文件系統(tǒng)對(duì)磁盤上的數(shù)據(jù)進(jìn)行編碼的方法是不同的永罚。v-node是一種抽象機(jī)制啤呼,因此無論底層文件系統(tǒng)實(shí)現(xiàn)如何,都有一種訪問該信息的統(tǒng)一方法呢袱,而i節(jié)點(diǎn)存儲(chǔ)特定的訪問信息官扣。
查看v-node和i-node之間區(qū)別的另一種方法是,v-node就像文件系統(tǒng)中存在的文件一樣羞福。抽象地說惕蹄,它可以是任何東西并存儲(chǔ)在任何設(shè)備上—甚至可能不是一個(gè)文件,比如/dev/urandom或/dev/zero治专。相反卖陵,i-node描述如何訪問該文件,包括存儲(chǔ)在哪個(gè)設(shè)備上以及設(shè)備特定的讀/寫過程张峰。
在Linux和許多Unix系統(tǒng)上泪蔫,v-node不是顯式使用的,而是只有i-node喘批;并且撩荣,i-node可以達(dá)到雙重目的。i-node既可以是文件的通用抽象表示形式谤祖,類似v-node婿滓,也可以存儲(chǔ)設(shè)備特定的指令老速。我們將繼續(xù)討論v-node/i-node的區(qū)別粥喜,因?yàn)樗喕嗽S多概念。
3. 復(fù)習(xí)打開文件
既然我們對(duì)內(nèi)核數(shù)據(jù)結(jié)構(gòu)有了更好的了解橘券,那么讓我們來回顧一下文件描述符的一些常用用法额湘,以及它如何與我們對(duì)內(nèi)核數(shù)據(jù)結(jié)構(gòu)的理解相匹配卿吐。
3.1 標(biāo)準(zhǔn)文件描述符
標(biāo)準(zhǔn)文件描述符由getty創(chuàng)建,并與終端設(shè)備驅(qū)動(dòng)程序相關(guān)聯(lián)锋华,而且通過使用read()和write()嗡官,像其他打開文件一樣使用標(biāo)準(zhǔn)文件描述符。它們必須在進(jìn)程表和文件表中以及v-nodes中有條目毯焕。
然而衍腥,標(biāo)準(zhǔn)的文件描述符不是磁盤上的文件,而是與另一個(gè)設(shè)備纳猫,終端設(shè)備相關(guān)聯(lián)的文件婆咸。這意味著v-node條目是指終端設(shè)備i-node,它存儲(chǔ)使用戶能夠從終端讀寫的底層訪問功能芜辕。
3.2 打開新文件
當(dāng)我們用open()打開一個(gè)新文件時(shí)尚骄,會(huì)生成一個(gè)新的文件描述符, usually one more than the last file descriptor侵续。還在文件描述符表中提供了一個(gè)新條目倔丈,按文件描述符號(hào)進(jìn)行索引,并在打開的文件表中創(chuàng)建一個(gè)新條目状蜗。
如果此文件存在于磁盤上需五,則文件表?xiàng)l目將引用一個(gè)v-node,該節(jié)點(diǎn)引用可以從磁盤或存儲(chǔ)該文件的特定設(shè)備讀取/寫入數(shù)據(jù)的i-node信息轧坎。
3.3 父子進(jìn)程共享文件
當(dāng)進(jìn)程fork時(shí)警儒,整個(gè)進(jìn)程表?xiàng)l目將被復(fù)制,包括所有打開的文件描述符眶根。
但是蜀铲,文件表?xiàng)l目中沒有重復(fù)。父級(jí)和子級(jí)中的文件描述符引用相同的文件表項(xiàng)属百。請(qǐng)注意记劝,文件偏移量存儲(chǔ)在文件表?xiàng)l目中,因此當(dāng)一個(gè)進(jìn)程從文件中讀取時(shí)族扰,它會(huì)移動(dòng)偏移量厌丑,而當(dāng)另一個(gè)進(jìn)程從文件讀取時(shí),它將開始從第一個(gè)進(jìn)程停止的位置讀取渔呵。
父進(jìn)程和子進(jìn)程都有不同的進(jìn)程表項(xiàng)怒竿,但共享一個(gè)文件表項(xiàng)。子程序中的每一次讀取都表示條目中的偏移量扩氢,而父項(xiàng)中的偏移量也是相同的耕驰。結(jié)果是父級(jí)和其子級(jí)在讀取文件中的每個(gè)字節(jié)之間交替進(jìn)行。
3.4 復(fù)制文件
我們還研究了使用dup2()復(fù)制文件的問題录豺,這在內(nèi)核數(shù)據(jù)結(jié)構(gòu)中也有一個(gè)表示朦肘》构回想一下,dup2()會(huì)將一個(gè)文件描述符復(fù)制到另一個(gè)文件描述符上媒抠。
在內(nèi)核數(shù)據(jù)結(jié)構(gòu)中弟断,這意味著文件描述符表中的兩個(gè)條目引用相同的文件表?xiàng)l目。結(jié)果趴生,對(duì)文件描述符中的任何一個(gè)的讀取和寫入相同阀趴,如中所示,它們引用相同的文件苍匆。
3.5 管道
管道更像標(biāo)準(zhǔn)的文件描述符舍咖,因?yàn)樗鼈儾灰梦募到y(tǒng)中的文件,而是用于在管道的寫讀端之間輸送數(shù)據(jù)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)锉桑。
對(duì)pipe()的調(diào)用將創(chuàng)建兩個(gè)文件描述符排霉,即管道兩端的讀和寫文件描述符。這些文件描述符中的每一個(gè)都將在文件表中包含條目民轴,但是v-node條目通過內(nèi)核緩沖區(qū)鏈接攻柠。