Linux文件系統(tǒng)之inode與軟硬鏈接詳解
一、前言
今天討論的話題是沒有被打開的文件。文件等于文件內(nèi)容加文件屬性,沒打開的文件一定是存儲在磁盤上的,并且 Linux 是將文件的屬性和內(nèi)容分開存儲。文件內(nèi)容以數(shù)據(jù)塊的形式進行存儲,文件屬性以 inode 的形式進行存儲。
二、認識硬件——磁盤
我們這里說的磁盤指的是機械磁盤,并非我們現(xiàn)在我們筆記本上使用的 SSD。機械磁盤是計算機上唯一的一個機械設(shè)備,也是一個外設(shè)。
小Tips:磁頭是一面一個,磁頭與盤面不接觸。磁頭通過向盤面進行充放電來完成數(shù)據(jù)的寫入。磁盤叫做永久性存儲介質(zhì),內(nèi)存叫做掉電易失性存儲介質(zhì)。
2.1 磁盤的存儲構(gòu)成
每一個盤面由多個磁道構(gòu)成,一個磁道又有多個扇區(qū)構(gòu)成。磁盤被訪問的最基本單元是扇區(qū),一般扇區(qū)的大小是 512 字節(jié),有的是 4KB。要修改磁盤中 1 字節(jié)的數(shù)據(jù),需要把該字節(jié)所在的扇區(qū)都加載到內(nèi)存中??梢园汛疟P看成是由無數(shù)個扇區(qū)構(gòu)成的存儲介質(zhì)。要把數(shù)據(jù)存儲到磁盤,第一個需要解決的問題就是如何定位一個扇區(qū),首先需要定位盤面,也就是確定用哪個磁頭,因為一個磁頭對應(yīng)一個盤面,接下來需要定位磁道,最后定位扇區(qū)。所有的磁頭都是同步運動的,在某一時刻,從從上向下看去,以磁頭所在點為半徑的不同盤面上的磁道就會形成一個叫做柱面的結(jié)構(gòu)。磁頭運動主要是去定位磁道,盤面旋轉(zhuǎn)主要是去定位扇區(qū),磁頭的定位是由硬件電路進行控制。由此可見,磁盤的讀取效率取決于磁頭、盤面的運動速度和運動次數(shù),運動越少,效率越高;運動越多,效率越低。因此,在軟件設(shè)計上要求設(shè)計者一定要有意識的將相關(guān)數(shù)據(jù)放在一起。
2.2 磁盤的邏輯抽象
最終一個磁盤可以看作是基于扇區(qū)的數(shù)組,每一個扇區(qū)都對應(yīng)有一個下標(biāo)來唯一標(biāo)識。通過這個下標(biāo)(LBA 邏輯扇區(qū)地址),再結(jié)合每一面磁道的個數(shù)和每一個磁道上扇區(qū)的個數(shù)就可以定位到該扇區(qū)在磁盤上的位置(CHS地址)。
小Tips:不僅 CPU 有寄存器,其它外設(shè)也有,磁盤中也有寄存器。比如:控制寄存器,用來存儲 CPU 下發(fā)的讀寫指令;數(shù)據(jù)寄存器,存儲要寫入的磁盤的數(shù)據(jù);地址寄存器,存儲 CPU 傳送來的 LBA 地址;狀態(tài)寄存器,存儲磁盤的狀態(tài),操作系統(tǒng)通過檢查該狀態(tài)寄存器去判斷讀寫是否成功。
三、操作系統(tǒng)對磁盤的使用
上圖為 Linux ext2 磁盤文件系統(tǒng)圖(內(nèi)核內(nèi)存映像肯定有所不同),磁盤是典型的塊設(shè)備,操作系統(tǒng)首先會對磁盤進行分區(qū),就如我們電腦中的 C 盤和 D 盤。接著,磁盤分區(qū)被劃分為若干個塊組(Block group),每個塊組中有許多塊(block),一個 block 的大小是由格式化的時候確定的,并且不可以更改,常見的是 4KB,即 4096字節(jié)。
- Boot Block:通常存儲操作系統(tǒng)啟動的相關(guān)信息,比如:操作系統(tǒng)在什么位置、當(dāng)前磁盤一共被劃分成了多少個分區(qū)等。這些信息一般存儲在磁盤的最前面,當(dāng)然為了防止意外,這些內(nèi)容在其它地方也會有備份。
- Block Group:ext2 文件系統(tǒng)會根據(jù)分區(qū)的大小劃分為數(shù)個 Block Group。而每個 Block Group 都有著相同的結(jié)構(gòu)組成。
- Super Block:存放文件系統(tǒng)本身的信息,這里面記錄了整個分區(qū)的信息。例如:整個分區(qū)有多大、該分區(qū)里面每組的起始位置、每個組的大小、每個組的 inode 數(shù)量、每個組的 block 數(shù)量、每個組的其實 inode 編號、block 和 inode 的總量,未使用的 block 和 inode 的數(shù)量,一個 block 和 inode 的大小、文件系統(tǒng)的類型與名稱、最近一次掛載的時間、最近一次寫入數(shù)據(jù)的時間、最近一次檢驗磁盤的時間、該文件系統(tǒng)所擁有的字段以及字段的存儲順序和起始位置(也就是規(guī)定了一個組的空間劃分)等其它文件系統(tǒng)的相關(guān)信息。Super Block 的信息被破壞,可以說整個文件系統(tǒng)結(jié)構(gòu)就被破壞了。該字段并不是在每一個分組中都有,而是在部分組里面有,防止意外發(fā)生,操作系統(tǒng)通過“魔數(shù)”來判斷是否是 Super Block。
- Group Descriptor Table:塊組描述符,存儲塊組屬性信息,例如:當(dāng)前分組的大小、使用情況、下一個文件描述符應(yīng)該從哪開始。
- Block Bitmap:塊位圖,將比特位的位置和塊號映射起來,里面記錄著 Data Block 中哪個數(shù)據(jù)塊已經(jīng)被占用,哪個數(shù)據(jù)塊沒有被占用。
- inode Bitmap:每個 bit 表示一個 inode 是否空閑可用。
- inode Table:一組 inode。每一個 inode 用來存放單個文件的所有屬性,如:文件大小、所有者、最近修改時間等。每個 inode 的大小一般是 128字節(jié),且每一個 inode 都有唯一的編號。一般而言,一個文件一個 inode。
- Data block:數(shù)據(jù)區(qū),存放文件內(nèi)容。以塊的形式呈現(xiàn),常見塊的大小是 4KB。每個塊都有自己獨一無二的塊號。
小Tips:操作系統(tǒng)在訪問磁盤的時候,會以塊為基本單位進行訪問。
格式化:每個組的前四個字段存儲的都是一些文件系統(tǒng)的屬性信息或者分組的使用情況信息,這些內(nèi)容應(yīng)該在我們使用磁盤之前都準(zhǔn)備好。所以,每一個分區(qū)在被使用前,都必須將部分文件系統(tǒng)的屬性信息提前設(shè)置進對應(yīng)的分區(qū)中,方便后續(xù)對分區(qū)和分組的使用,這個動作就叫做格式化。
在 Linux 中,文件的屬性里面是不包含文件的名稱。在 Linux 系統(tǒng)里面標(biāo)識文件用的是 inode 編號。一個 inode 表示一個文件的所有屬性,文件名并不屬于 inode 內(nèi)的屬性。
一個 inode 與 數(shù)據(jù)塊的對應(yīng)關(guān)系:
其中直接索引對應(yīng)的塊中存儲的就是文件內(nèi)容,二級索引對應(yīng)的塊中存儲的不是文件內(nèi)容而是塊號。假設(shè)塊的大小是 4KB,塊號用 4字節(jié)。那么一個塊就可以存儲 1024 個塊號,這 1024 個塊號對應(yīng)的塊里面存儲的是文件的內(nèi)容,三級索引同理。這樣做的目的是在 inode 里面用較少的空間就可以映射出更多的數(shù)據(jù)塊。
小Tips:inode 編號是以分區(qū)為單位進行統(tǒng)一分配的,而且不能跨分區(qū),即每個分區(qū)中的 inode 編號都是從 0 開始,且一個分區(qū)中的 inode 個數(shù)是有上限的,因此可能會存在一下情況:一個分區(qū)中的 inode 被用完了,但是數(shù)據(jù)塊還沒有被用完,這種情況對應(yīng)的就是創(chuàng)建了非常多的文件,但是每個文件的內(nèi)容非常小;一個分區(qū)中的數(shù)據(jù)塊被用完了,但是 inode 還沒有被用完,這種情況就是創(chuàng)建的文件并不多,但是每個文件的大小非常大。
3.1 再來理解創(chuàng)建文件
首先創(chuàng)建文件一定是在一個路徑下(目錄)進行創(chuàng)建,這個路徑就會幫我們定位到一個分區(qū),然后去從第一個分組開始查看當(dāng)前分組的 GDT 字段,看該分組中 inode 的使用情況,若當(dāng)前分組中的 inode 還有剩余,接著去讀取 inode_Bitmap,獲取最近一個未被使用的 inode 編號,然后拿著 inode 編號去 inode_Table 里面找到對應(yīng)的 inode,將文件的屬性信息一填。如果有文件內(nèi)容,先拿著 inode 編號找到對應(yīng)的分組,根據(jù)寫入內(nèi)容的大小去 Block_Bitmap 中找出對應(yīng)數(shù)量未被使用的塊號,然后將這些塊號寫入到 inode 對應(yīng)的屬性里面,然后拿著塊號去 Data blocks 中進行寫入。
3.2 再來理解刪除文件
刪除文件只要拿著該文件的 inode 編號,在 inode Table 中找到對應(yīng)的 indoe,獲取到里面的 blocks,即拿到該文件對應(yīng)的所有塊號,然后根據(jù)這些塊號將 Block Bitmap 中對應(yīng)的比特位置0(假設(shè) 0 表示對應(yīng)的塊未被使用)。最后再根據(jù) inode 編號到 inode Bitmap 中將該 inode 對應(yīng)的比特位置為0,至此,一個文件就被刪除啦。可以發(fā)現(xiàn)從頭到尾并沒有去修改塊中的內(nèi)容,這也是為什么拷貝 4G 的文件很慢,刪 4G 的文件很快。所以在理論上,一個被刪除的文件,可以根據(jù) inode 將其恢復(fù)出來。
總結(jié):刪文件就是去修改 inode_Bitmap 和 Block_Bitmap 中的字段。在計算機領(lǐng)域的刪除并不等于清空,大部分情況下,刪除都表示可覆蓋。因為清空會導(dǎo)致效率大大下降。
3.3 再來理解目錄
上面說的所有對文件的操作都離不開 inode 編號,但是我們作為普通用戶平時好像也并沒有關(guān)注過 inode 編號,我們一般是直接使用文件名,此時就必須再來理解一下目錄了。目錄也是文件,也有自己的 inode,目錄也有屬性。目錄也有(數(shù)據(jù)塊),目錄的數(shù)據(jù)塊里面存放的是這個目錄下的所有文件名和該文件名對應(yīng)的 inode 編號的映射關(guān)系,這是一種 key-value 結(jié)構(gòu),這就是為什么一個目錄里面不允許出現(xiàn)同名文件。與此同時,和目錄有關(guān)的一些歷史問題也得到了解決,對于一個目錄,沒有 w,我們無法在該目錄下創(chuàng)建文件,本質(zhì)就是我們不能向目錄對應(yīng)的數(shù)據(jù)塊中寫入文件名和 inode 的對應(yīng)關(guān)系;沒有 r,無法產(chǎn)看該目錄下的文件,本質(zhì)就是不能讀取目錄文件的數(shù)據(jù)塊。因此我們在查找一個文件時,首先需要知道該文件所在目錄的 inode,要知道目錄文件的 inode 編號,就需要知道目錄文件所在目錄的 inode 編號,如此遞歸一直到根目錄,這就是為什么我們在操作一個任何一個文件的時候都需要知道它的絕對或者相對路徑。如果每操作一個文件都要去這樣遞歸一層層的查找,那么效率是非常低的。因此在 Linux 操作系統(tǒng)中有一個叫做 dentry 緩存(目錄項緩存),里面記錄了該用戶經(jīng)常訪問的文件名和 inode 編號之間的映射關(guān)系。
四、硬鏈接
// 創(chuàng)建硬鏈接的指令 ln test.txt hard-link
硬連接不是一個獨立的文件,因為它沒有獨立的 inode。所謂建立硬連接,本質(zhì)其實就是在特定目錄的數(shù)據(jù)塊中新增文件名和指向的文件的 inode 編號的映射關(guān)系。上圖也可以證明文件名不是 inode 中的屬性,因為一個 inode 編號對應(yīng)一個 inode,如果文件名是 inode 中的屬性,那么上圖中編號 1978740 的文件不可能對應(yīng)兩個文件名。
小Tips:任意一個文件,無論是目錄,還是普通文件,都有 inode,每一個 inode 內(nèi)部,都有一個叫做引用計數(shù)的計數(shù)器,這個計數(shù)器記錄了有多少個文件名“指向”該文件(由硬鏈接可以得知,在 Linux 中可以讓多個文件名對應(yīng)于同一個 inode)。完整的刪除文件過程就是先將特定目錄數(shù)據(jù)塊中的文件名與 inode 的映射關(guān)系刪除,然后根據(jù) inode 的編號,將對應(yīng) inode 中的引用計數(shù)減減,最終看其是否減到零,減到零再執(zhí)行 3.2 小結(jié)的步驟。此外,創(chuàng)建一個普通文件,它的硬鏈接數(shù)默認是1。
創(chuàng)建目錄文件的默認鏈接數(shù)為什么是 2 ?
硬鏈接數(shù)為2,說明與該文件 inode 編號有關(guān)的映射關(guān)系有兩個,其中一個映射關(guān)系保存在 dir 所在目錄文件的數(shù)據(jù)塊,即 2023-11-06 目錄文件中的數(shù)據(jù)塊中,另外一個保存在 dir 目錄自身的數(shù)據(jù)塊中。
根目錄的硬鏈接數(shù)減2就是根目錄下創(chuàng)建的目錄個數(shù)。根目錄稍微有一點特殊,根目錄中的 .
和 ..
文件名對應(yīng)的 inode 編號是一樣的,都是根目錄。除去這倆文件名和 inode 編號的映射關(guān)系外,剩下的硬鏈接數(shù)就表示根目錄下創(chuàng)建的目錄個數(shù),剩下的 18 個就是根目錄中所有目錄文件中 ..
與根目錄 inode 編號的鏈接關(guān)系。可以這樣計算的本質(zhì)原因就是,根目錄下所有目錄中的 ..
都一定是指向根目錄的,對應(yīng)根目錄的 inode 編號(也就是上圖中的 2 )。這種計算方法也可以推行到其它任意目錄。
總結(jié):建立硬鏈接就是給一個已存在的文件創(chuàng)建別名,硬鏈接通常用來進行路徑定位,采用硬鏈接,可以進行目錄間切換。
小Tips:Linux 系統(tǒng)不允許用戶對目錄文件建立硬連接。只要是為了避免在文件搜索的時候發(fā)生環(huán)路問題,系統(tǒng)在搜索文件的時候并不會搜索 .
和 ..
,如果允許用戶為目錄文件創(chuàng)建硬鏈接,那么操作系統(tǒng)在進行文件搜索的時候就無法避免環(huán)路問題。
五、軟鏈接
// 創(chuàng)建軟鏈接的指令 ln -s file.txt soft-link
軟鏈接是一個獨立的文件,有獨立的 inode,也有獨立的數(shù)據(jù)塊,它的數(shù)據(jù)塊里面存的是指向文件的路徑。軟鏈接非常像 Windows 中的快捷方式。軟鏈接的使用場景:一般發(fā)布的可執(zhí)行程序可能存在一個較深的路徑下面,要執(zhí)行它的話就需要帶很長一串路徑,顯得十分麻煩,此時我們就可以創(chuàng)建一個軟鏈接指向該可執(zhí)行文件,之后要想運行該可執(zhí)行程序就直接去執(zhí)行軟鏈接即可。
小Tips:可以對任意類型的文件創(chuàng)建軟鏈接。
// 刪除軟硬鏈接都可以用 unlink 指令 unlink soft-link
六、結(jié)語
以上就是Linux文件系統(tǒng)之inode與軟硬鏈接詳解的詳細內(nèi)容,更多關(guān)于Linux inode與軟硬鏈接的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Linux平臺Segmentation fault(段錯誤)調(diào)試過程
這篇文章主要介紹了Linux平臺Segmentation fault(段錯誤)調(diào)試過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09Apache中利用mod_rewrite實現(xiàn)防盜鏈
自從上次在博客中推薦《you are my everything》以后,服務(wù)器的流量突然多了起來,有幾次甚至導(dǎo)致了VPS的當(dāng)機。后來經(jīng)過分析:盜鏈這個MP3的網(wǎng)頁包括諸如QQ空間、校內(nèi)網(wǎng)空間、更有甚者還放到了Taobao小店、個人博客也有不少,全部作為了背景音樂,并且導(dǎo)致各種爬蟲瘋狂抓取這個文件。找到了問題原因就只有一個辦法了,就是利用Apache的mod_rewrite模塊把盜鏈行為拒之門外。2008-04-04Ubuntu下如何創(chuàng)建XFS文件系統(tǒng)的LVM詳解
這篇文章主要給大家介紹了關(guān)于在Ubuntu下如何創(chuàng)建XFS文件系統(tǒng)的LVM的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10CentOS7 配置Nginx支持HTTPS訪問的實現(xiàn)方案
這篇文章主要介紹了CentOS7 配置Nginx支持HTTPS訪問的實現(xiàn)方案的相關(guān)資料,這里實現(xiàn)該功能的步驟進行了詳解,需要的朋友可以參考下2016-11-11