關(guān)于文件系統(tǒng),相信大家都不陌生。身為攻城獅的我們幾乎天天都會與之打交道,但是細深剖一下,其中又有多少是我們理解深度不夠的呢。那么讓我們一起來看一下下面這一組 Linux 文件系統(tǒng)相關(guān)的問題吧:
1、機械磁盤隨機讀寫時速度非常慢,操作系統(tǒng)是采用什么技巧來提高隨機讀寫的性能的?
2、touch 一個新的空文件占用磁盤空間嗎?占用的話占用多少?
3、新建一個空目錄占用磁盤空間嗎?占用多少?和新建一個文件相比,哪個占用的更大?
4、你知道文件名是記錄在磁盤的什么地方嗎?
5、文件名最長多長?受什么制約?
6、文件名太長了會影響系統(tǒng)性能嗎?為什么會產(chǎn)生影響?
7、一個目錄下最多能建立多少個文件?
8、新建一個內(nèi)容大小 1 k 的文件,實際會占用多大的磁盤空間?
9、向操作系統(tǒng)發(fā)起讀取文件 2 Byte 的命令,操作系統(tǒng)實際會讀取多少呢?
10、我們使用文件時要怎么樣來能提高磁盤IO速度?
如果你能想也不用想的就回答上來百分八十的問題,那么請關(guān)掉本篇文章吧。如果不能,而且你也像作者一樣對有窺探操作系統(tǒng)隱私的嗜好,那么就請隨我一起來探索文件系統(tǒng)的這些有趣的地方,相信理解了這些之后對我們手中的工作會有很大的幫助。這篇文章實驗所用文件系統(tǒng)是 ext 系的。
一、磁盤構(gòu)成及分區(qū)
1、磁盤物理結(jié)構(gòu)
還是先從最基本的磁盤物理結(jié)構(gòu)說起吧,注意本文只討論機械磁盤,SSD 不在本文討論范圍之內(nèi)。我們?nèi)祟惞芾砣魏问挛锟偸橇?xí)慣先劃分出一定的結(jié)構(gòu),在此規(guī)則的基礎(chǔ)上進行管理。軍隊分軍、師、旅、團和營。公司分事業(yè)群、部門、中心和小組。然后對于管理磁盤,分磁盤面、磁頭、磁道、柱面和扇區(qū)。
-
磁盤面:磁盤是由一疊磁盤面組成,見圖。
-
磁頭(Heads):每個磁頭對應(yīng)一個磁盤面,負責該磁盤面上的數(shù)據(jù)的讀寫。。
-
磁道(Track):每個盤面會圍繞圓心劃分出多個同心圓圈,每個圓圈叫做一個磁道。
-
柱面(Cylinders):所有盤片上的同一位置的磁道組成的立體叫做一個柱面。
-
扇區(qū)(Sector):以磁道為單位管理磁盤仍然太大,所以計算機前輩們又把每個磁道劃分出了多個扇區(qū),見下右圖
本人愛上 Linux 的一個原因就是只要你愿意下功夫,你就能把 Linux 的內(nèi)部邏輯徹底鋪開來看,這點比 Windows 要好太多了。Linux 上可以通過 fdisk 命令,來查看當前系統(tǒng)使用的磁盤的這些物理信息。
以上是我本人的一臺虛擬機的磁盤物理信息??梢钥闯鑫业拇疟P有 255 個 heads,也就是說共有 255 個盤面。3263 個 cylinders,也就是說每個盤面上都有 3263 個磁道, 63 sectors/track 說的是每個磁道上共有 63 個扇區(qū)。命令結(jié)果也給出了 Sector size 的值是 512 bytes。那我們動筆算一下該磁盤的大小吧。
255 盤面 * 3263 柱面 * 63 扇區(qū) * 每個扇區(qū) 512 bytes = 26839088640 byte。
結(jié)果是 26.8 G,和磁盤的總大小基本相符(至于fdisk給出的詳細結(jié)果相差了約4M的大小,筆者也沒有弄徹底明白,有興趣的讀者可以繼續(xù)研究)。
不過要注意一點就是上面的盤面等數(shù)據(jù)是邏輯上的,是物理盤面映射轉(zhuǎn)化而來的,這個轉(zhuǎn)化關(guān)系我現(xiàn)在還沒搜到特別好的資料。
2、分區(qū)
分區(qū)是操作系統(tǒng)對磁盤進行管理的第一步,這也是我們?nèi)魏我粋€計算機使用者都非常熟悉的概念。例如Windows下的C、D、E、F盤。那么請思考一下,
思考:前面的磁盤的詳細物理結(jié)構(gòu)已經(jīng)有了,如果讓你把整塊磁盤分成C、D等分區(qū),你會怎么分呢?
-
方案一:255 個盤面,C 盤是 0-100 盤面, D 盤是 101-200 個盤面, ……
-
方案二:3263 個柱面,C 盤 0-1000 個柱面,D 盤 1001-20001 個柱面, ……
對于以上的兩個方案,你會選擇哪一種呢??先說下磁盤 IO 時的過程。
-
第一步,首先是磁頭徑向移動來尋找數(shù)據(jù)所在的磁道。這部分時間叫尋道時間。
-
第二步,找到目標磁道后通過盤面旋轉(zhuǎn),將目標扇區(qū)移動到磁頭的正下方。
-
第三步,向目標扇區(qū)讀取或者寫入數(shù)據(jù)。到此為止,一次磁盤 IO 完成。
故單次磁盤 IO 時間 = 尋道時間 旋轉(zhuǎn)延遲 存取時間。
對于旋轉(zhuǎn)延時,現(xiàn)在主流服務(wù)器上經(jīng)常使用的是1W轉(zhuǎn)/分鐘的磁盤,每旋轉(zhuǎn)一周所需的時間為60*1000/10000=6ms,故其旋轉(zhuǎn)延遲為(0-6ms)。對于存取時間,一般耗時較短,為零點幾 ms。對于尋道時間,現(xiàn)代磁盤大概在 3-15 ms,其中尋道時間大小主要受磁頭當前所在位置和目標磁道所在位置相對距離的影響。
其實采用哪一種,最主要看的是那種方式性能更快。因為同一分區(qū)下的數(shù)據(jù)經(jīng)常會一起讀取,假如采用第一種,那么這樣磁頭就需要在 3000 多個 track 間不停地跳來跳去,這樣磁盤的尋道時間就會翻倍,磁盤性能就會下降。
而對于方案二,假如對于磁盤C,只需要在磁頭在 1-1000 個磁道間移動就可以了,大大降低了尋道時間。(實際上分區(qū)并不是從 0 開始的,磁盤的第一個磁道對應(yīng)的柱面會被用來安裝引導(dǎo)加載程序以及磁盤分區(qū)表)。所以,方案二的分區(qū)方式可以降低磁盤 IO 時間中的尋道時間部分,所以所有的操作系統(tǒng)采用的都是方案二,沒有用方案一的。
在Linux下使用過fdisk進行分區(qū)的話可以注意到以下信息。
這充分證明了操作系統(tǒng)是采用方案二的。
回到開篇問題 1,操作系統(tǒng)是采用什么技巧來降低隨機讀寫的性能問題的呢?操作系統(tǒng)通過按磁道對應(yīng)的柱面劃分分區(qū),來降低磁盤 IO 所花費的的尋道時間 ,進而提高磁盤的讀寫性能。
二、目錄與文件
1、引子
好了,磁盤基礎(chǔ)都說完了,那我們正式進入主題,開始我們 Linux 文件系統(tǒng)相關(guān)的討論吧。文件系統(tǒng)不就是目錄和文件嗎?這二位可是我們熟悉的不能再熟悉的家伙了。可你確認它不是你的那位熟悉的陌生人么?我先來來創(chuàng)建個空目錄和空文件吧,查看結(jié)果如下圖:
我們都知道第五列顯示的是占用的空間大小,那么我來提個幾個小小的問題吧。
-
1)為什么目錄占用的空間是 4096?
-
2)為什么空文件占用的空間卻是 0?
-
3)如果空文件真占用 0 byte 空間,那么該文件的文件名、創(chuàng)建者以及權(quán)限-rw-rw-r—等文件夾相關(guān)的信息都存到哪兒去了?
2、我就不信空文件不占用空間
為了解開這個謎底,需要借助 df 命令。輸入 df –i,
Linux 結(jié)果中紅框位置處顯示的是 inodes 的相關(guān)信息,如果你對 inode 的概念不熟悉,你可以暫時把它當成一個操作系統(tǒng)秘密管理的一個家伙,會占用空間就行了。接下來我 touch 一個空的文件后再次 df -i。
雖然前面操作系統(tǒng)告訴我們,一個新建的空文件占用的空間是 0。但是這個實驗卻證明操作系統(tǒng)“欺騙”了我們,它消耗掉了一個 inode。那么 inode 的節(jié)點大小是多少呢,使用 dumpe2fs 命令可以幫助我們查看到這個東東的實際大小。在輸出的結(jié)果中我們可以找到下面這行。
它告訴我們每個 inode 的大小是 256 Byte。當然這個大小每臺機器都會不一樣,它實際上是在系統(tǒng)格式化磁盤的時候決定的。
好了,開篇第二個問題也有答案了。原來新建一個空的文件是會占用磁盤空間的,實際占用的是 256 Byte。哦,不,準確的說法應(yīng)該是一個 inode size,具體的值是在格式化時決定的。
再說說新建空目錄吧,前面說了新建空目錄會占用4KB的磁盤空間。那么僅僅如此嗎?我們同樣在新建目錄前后都使用df –i來監(jiān)視系統(tǒng) inode 的占用。
原來目錄也是會占用一個 inode 節(jié)點的,第三個問題也有了答案了,新建一個空目錄會占用磁盤空間 4KB inode size。哦,這個在你的系統(tǒng)上也不一定是4K,它實際上一個 block size。同樣在 dumpe2fs 下可以看到。
只不過我的磁盤在格式化時采用的是 4KB 的大小,呵呵!
3、神秘的空目錄的4KB
前面的謎團解開了,可以作為攻城獅的我對另外一個東西產(chǎn)生了好奇心。就是空目錄占用的那 4KB,這些空間是用來存什么的呢?好神秘呀。cd 到我們新建的目錄下查看。
我們再新建兩個空的文件,再查看下目錄的空間占用情況。
貌似,沒有什么新發(fā)現(xiàn)。因為空文件不占用 block,所以這里顯示的仍然是目錄占用的 block,和之前大小沒有變化。那么我繼續(xù)使用 php 腳本創(chuàng)建 100 個文件名長度為 32Byte 的空文件。
這時我們發(fā)現(xiàn)目錄占用的磁盤空間變大了,成了 3 個 Block 了。哈哈,這就解答了我們開篇的第四個問題,文件名是存在目錄占用的 block 中的。接下來我又還證明了每個目錄 block 中能保存的文件名個數(shù)是和文件名的長度有關(guān)的(好像有點廢話的意思,不過親手證明自己的猜想還是有點小爽的)。我又另外新建了個空目錄,創(chuàng)建了 100 個文件名長度為 32*3 個空文件,該臨時目錄占用的磁盤空間如下:
你可能會問我為什么文件名變成了 3 倍后,占用的 block 數(shù)目為什么沒有變成 3 倍。其實Linux文件系統(tǒng)關(guān)于文件的結(jié)構(gòu)體中除了文件名以外,還有其它的一些字段的,文件名變長3倍不會導(dǎo)致結(jié)構(gòu)體變大 3 倍的,這點可以參考 Linux 系統(tǒng)內(nèi)核相關(guān)書籍。
好了,到現(xiàn)在開篇問題 6 也有了答案了。文件名長了當然會對系統(tǒng)性能產(chǎn)生影響,因為這可能會導(dǎo)致更多的磁盤 IO。很多程序員都喜歡將文件命名為有意義的長串,使人一看文件名就知道用途。當然我沒說這樣不好,但是如果你的文件數(shù)量相當大的時候,你就要考慮你的文件名是否導(dǎo)致你的目錄 block 占用太多了。
占用的空間倒是小事,磁盤很便宜,但是你得考慮下在目錄下查找文件時操作系統(tǒng)的感受,操作系統(tǒng)可需要用你你提供的文件名進行字符串比較,而且運氣不好的話需要將其名下所有 block 都搞一遍才行啊。(當然了,你的文件名長度不變態(tài),而且數(shù)量沒有達到十萬數(shù)量級的話實際上這個開銷也不會太大,但是這個開銷你還是知道的為好)
至于開篇問題 5,文件名最長多長。實際上Linux操作系統(tǒng)就是為了避免程序員不節(jié)制地使用長文件名,強加了個限制,不得超過 255 byte。
另外,大家有沒有經(jīng)驗,在目錄下文件很多的時候,我們使用ls命令時會很慢?,F(xiàn)在大家知道原因了吧,這時實際上操作系統(tǒng)在讀取當前目錄的所有 block ,如果 block 比較多的話,可能得需要多次 IO 操作才能完成這個簡單的 ls 命令。
我在自己的電腦某個目錄下創(chuàng)建了一 100W 個空文件,ls 命令 1 分鐘還沒出結(jié)果,被我 ctrl c 掉了。在自己的項目中可不要這么干,雖然操作系統(tǒng)可以 cache 住你的目錄數(shù)據(jù),使你下次調(diào)用時會快很多,但我還是建議你單個目錄下文件數(shù)目不要過萬。否則你的程序在重啟后首次運行時可能會出現(xiàn)性能不佳的情況。
好了,回到開篇問題 7,你有答案了嗎?一個目錄下最多能建多少個文件,這個最多其實是受限于你目錄所在分區(qū)的 inode 數(shù)量,你有 100W 個 inode,你最多就可以新建 100W 個文件。但是,上面說了,單個目錄下文件數(shù)量最好不要過萬,否則會帶來系統(tǒng)性能的問題。
4、文件的block
再做個關(guān)于文件的實驗。我新建了個空目錄,并在其下新建了個文件,里面只寫了一個空格數(shù)據(jù),保存后 du 命令顯示如下:
這 8K 里有 4K 是目錄的,也就可以算出操作系統(tǒng)為只包含一個空格的文件分配了 4KB。其實文件的 block 比較簡單的了,不像目錄的 block 里會存很多文件系統(tǒng)的結(jié)構(gòu)體,文件的 block 里只會保存文件的數(shù)據(jù)。上面這個實驗表明,操作系統(tǒng)分配空間時是以 block 為最小單位。
也就是說只要你的文件數(shù)據(jù)不為空,操作系統(tǒng)就至少會給你分配一個 block 來存儲,直到你超過了 4KB,操作系統(tǒng)再給你分配下一個 block,就是這樣。所以對于開篇問題 8,新建一個內(nèi)容大小為 1k 的文件,實際會占用 1個 block(一般為4k)和一個 inode(一般為256byte)。
其實文件系統(tǒng)在向磁盤發(fā)起 IO 請求的時候,也是以 block size 為單位的。哪怕你只向操作系統(tǒng)發(fā)起讀取文件的 2 Byte,但是操作系統(tǒng)會一次性給你讀取 4KB 回來。因此磁盤 IO 真的是很慢,而且我們只要訪問了這 2 Byte,確實很有可能接下來繼續(xù)訪問這 2byte 后面的內(nèi)容,這也就是程序局部性原理,所以操作系統(tǒng)索性一次性就多讀取些回來了。呵呵,這就是開篇問題9的答案。
這就像我們?nèi)ス涑?,逛一次真的是很浪費時間,這可要比坑爹的磁盤 IO 也慢許多了。我們總不會逛了一圈超市就買了一個蘋果就回來了吧,我們肯定會多買些東西為家里以后的需求準備著,反正買一堆東西比買一個蘋果也沒多花多少時間,何樂為不為呢,就是這個道理。
再說說開篇問題 10,我們攻城獅怎么樣設(shè)計你的文件能提高一些 IO 速度呢?那就是如果你知道你的要新建的文件大概會占用多大的空間的話,比如 1M。那么你新建文件時就順便和操作系統(tǒng)說一下,讓它幫你將文件的 size 預(yù)留下來。這樣實際上操作系統(tǒng)時會盡可能為你分配連續(xù)的 block,這樣你再讀取這個文件時,磁頭就省去很多尋道時間了,IO 速度就顯得快多了。
三、寫在后面的話
前面我們說的都是基于我自己的文件系統(tǒng),情形是一個 block size 是 4KB,一個 inode size 是 256byte,包括我虛擬機上的 inode 數(shù)量才只有 140 多萬個。這些值實際上不是固定的,你完全可以在格式化你的硬盤的時候設(shè)置成其它的值。設(shè)置的原則就是看你的硬盤的容量,以及你的用途。
如果你的文件都是大于 4KB,甚至是幾 M,幾 G 的文件,那么建議你的 block 還是盡可能的大一點吧,這樣 inode 里就能少記幾個地址。
如果你的文件大部分都是1K以下的,那么確實使用4K的block會造成一點點浪費,如果你的老板對成本要求異??量痰脑?,你可以適當考慮把你的 block 設(shè)置得小一點。
另外,要關(guān)注你的文件系統(tǒng)的 inode。操作系統(tǒng)在查看目錄和文件占用的磁盤空間信息時把inode節(jié)點的占用給隱藏起來了,其用意在于為用戶提供一個白盒的環(huán)境,把數(shù)據(jù)占用的空間交給我們來認知,而把 inode 信息隱藏起來為了降低我們理解操作系統(tǒng)的難度。
而實際上,我們作為非普通用戶的開發(fā)人員應(yīng)該具備這個知情權(quán)。這個東東直接關(guān)系到你文件系統(tǒng)能創(chuàng)建文件數(shù)量。否則哪天等你發(fā)現(xiàn)線上機器磁盤還剩大把大把的空間,但就是 inode 使用光了,那時候就只有重新格式化或者遷移服務(wù)器了。這兩個操作想想都覺得苦逼啊,還是能避免就盡量避免吧。
思考題:我們大家有個經(jīng)驗就是目錄下小文件太多的情況下,往其它地方拷貝的話,速度會非常的慢,我們這時往往會把目錄壓縮一下再拷貝?,F(xiàn)在你能說出這樣做為什么會快嗎?
最后我想再多說一句,這篇九年的時寫的文章現(xiàn)如今看起來還是很有實用價值。
- EOF -
本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。