一口氣搞懂「文件系統」,就靠這 20 張圖了

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8e/8e32ced15780434b197e7bebe24e2671.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不多 BB,直接上「硬菜」。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/47/477dd4a8ce8bbadf1bdaf3a5e1f11776.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"正文"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"文件系統的基本組成"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件系統是操作系統中負責管理持久數據的子系統,說簡單點,就是負責把用戶的文件存到磁盤硬件中,因爲即使計算機斷電了,磁盤裏的數據並不會丟失,所以可以持久化的保存文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件系統的基本數據單位是文件,它的目的是對磁盤上的文件進行組織管理,那組織的方式不同,就會形成不同的文件系統。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Linux 最經典的一句話是:「"},{"type":"text","marks":[{"type":"strong"}],"text":"一切皆文件"},{"type":"text","text":"」,不僅普通的文件和目錄,就連塊設備、管道、socket 等,也都是統一交給文件系統管理的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Linux 文件系統會爲每個文件分配兩個數據結構:*"},{"type":"text","marks":[{"type":"italic"}],"text":"索引節點("},{"type":"text","text":"index node"},{"type":"text","marks":[{"type":"italic"}],"text":")和目錄項("},{"type":"text","text":"directory entry"},{"type":"text","marks":[{"type":"italic"}],"text":")"},{"type":"text","text":"*,它們主要用來記錄文件的元信息和目錄層次結構。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"索引節點,也就是 "},{"type":"text","marks":[{"type":"italic"}],"text":"inode"},{"type":"text","text":",用來記錄文件的元信息,比如 inode 編號、文件大小、訪問權限、創建時間、修改時間、"},{"type":"text","marks":[{"type":"strong"}],"text":"數據在磁盤的位置"},{"type":"text","text":"等等。索引節點是文件的"},{"type":"text","marks":[{"type":"strong"}],"text":"唯一"},{"type":"text","text":"標識,它們之間一一對應,也同樣都會被存儲在硬盤中,所以"},{"type":"text","marks":[{"type":"strong"}],"text":"索引節點同樣佔用磁盤空間"},{"type":"text","text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目錄項,也就是 "},{"type":"text","marks":[{"type":"italic"}],"text":"dentry"},{"type":"text","text":",用來記錄文件的名字、"},{"type":"text","marks":[{"type":"strong"}],"text":"索引節點指針"},{"type":"text","text":"以及與其他目錄項的層級關聯關係。多個目錄項關聯起來,就會形成目錄結構,但它與索引節點不同的是,"},{"type":"text","marks":[{"type":"strong"}],"text":"目錄項是由內核維護的一個數據結構,不存放於磁盤,而是緩存在內存"},{"type":"text","text":"。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於索引節點唯一標識一個文件,而目錄項記錄着文件的名,所以目錄項和索引節點的關係是多對一,也就是說,一個文件可以有多個別字。比如,硬鏈接的實現就是多個目錄項中的索引節點指向同一個文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,目錄也是文件,也是用索引節點唯一標識,和普通文件不同的是,普通文件在磁盤裏面保存的是文件數據,而目錄文件在磁盤裏面保存子目錄或文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目錄項和目錄是一個東西嗎?"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然名字很相近,但是它們不是一個東西,目錄是個文件,持久化存儲在磁盤,而目錄項是內核一個數據結構,緩存在內存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果查詢目錄頻繁從磁盤讀,效率會很低,所以內核會把已經讀過的目錄用目錄項這個數據結構緩存在內存,下次再次讀到相同的目錄時,只需從內存讀就可以,大大提高了文件系統的效率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,目錄項這個數據結構不只是表示目錄,也是可以表示文件的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那文件數據是如何存儲在磁盤的呢?"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"磁盤讀寫的最小單位是"},{"type":"text","marks":[{"type":"strong"}],"text":"扇區"},{"type":"text","text":",扇區的大小隻有 "},{"type":"codeinline","content":[{"type":"text","text":"512B"}]},{"type":"text","text":" 大小,很明顯,如果每次讀寫都以這麼小爲單位,那這讀寫的效率會非常低。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,文件系統把多個扇區組成了一個"},{"type":"text","marks":[{"type":"strong"}],"text":"邏輯塊"},{"type":"text","text":",每次讀寫的最小單位就是邏輯塊(數據塊),Linux 中的邏輯塊大小爲 "},{"type":"codeinline","content":[{"type":"text","text":"4KB"}]},{"type":"text","text":",也就是一次性讀寫 8 個扇區,這將大大提高了磁盤的讀寫的效率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上就是索引節點、目錄項以及文件數據的關係,下面這個圖就很好的展示了它們之間的關係:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0c/0c7d1e73a5eff79dbf126d10ba75e44b.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"索引節點是存儲在硬盤上的數據,那麼爲了加速文件的訪問,通常會把索引節點加載到內存中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,磁盤進行格式化的時候,會被分成三個存儲區域,分別是超級塊、索引節點區和數據塊區。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"超級塊"},{"type":"text","text":",用來存儲文件系統的詳細信息,比如塊個數、塊大小、空閒塊等等。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"索引節點區"},{"type":"text","text":",用來存儲索引節點;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"數據塊區"},{"type":"text","text":",用來存儲文件或目錄數據;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們不可能把超級塊和索引節點區全部加載到內存,這樣內存肯定撐不住,所以只有當需要使用的時候,纔將其加載進內存,它們加載進內存的時機是不同的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"超級塊:當文件系統掛載時進入內存;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"索引節點區:當文件被訪問時進入內存;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"虛擬文件系統"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件系統的種類衆多,而操作系統希望"},{"type":"text","marks":[{"type":"strong"}],"text":"對用戶提供一個統一的接口"},{"type":"text","text":",於是在用戶層與文件系統層引入了中間層,這個中間層就稱爲*"},{"type":"text","marks":[{"type":"italic"}],"text":"虛擬文件系統("},{"type":"text","text":"Virtual File System,VFS"},{"type":"text","marks":[{"type":"italic"}],"text":")。"},{"type":"text","text":"*"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"VFS 定義了一組所有文件系統都支持的數據結構和標準接口,這樣程序員不需要了解文件系統的工作原理,只需要瞭解 VFS 提供的統一接口即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Linux 文件系統中,用戶空間、系統調用、虛擬機文件系統、緩存、文件系統以及存儲之間的關係如下圖:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b9/b92499b17ed11bc279689ecdb6efb4b6.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Linux 支持的文件系統也不少,根據存儲位置的不同,可以把文件系統分爲三類:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"磁盤的文件系統"},{"type":"text","text":",它是直接把數據存儲在磁盤中,比如 Ext 2/3/4、XFS 等都是這類文件系統。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"內存的文件系統"},{"type":"text","text":",這類文件系統的數據不是存儲在硬盤的,而是佔用內存空間,我們經常用到的 "},{"type":"codeinline","content":[{"type":"text","text":"/proc"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"/sys"}]},{"type":"text","text":" 文件系統都屬於這一類,讀寫這類文件,實際上是讀寫內核中相關的數據數據。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"網絡的文件系統"},{"type":"text","text":",用來訪問其他計算機主機數據的文件系統,比如 NFS、SMB 等等。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件系統首先要先掛載到某個目錄纔可以正常使用,比如 Linux 系統在啓動時,會把文件系統掛載到根目錄。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"文件的使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們從用戶角度來看文件的話,就是我們要怎麼使用文件?首先,我們得通過系統調用來打開一個文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bb/bb6dc369fbf16afbdb088c6c7c26d9cc.png","alt":"write 的過程","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"c"},"content":[{"type":"text","text":"fd = open(name, flag); # 打開文件\n...\nwrite(fd,...); # 寫數據\n...\nclose(fd); # 關閉文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面簡單的代碼是讀取一個文件的過程:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先用 "},{"type":"codeinline","content":[{"type":"text","text":"open"}]},{"type":"text","text":" 系統調用打開文件,"},{"type":"codeinline","content":[{"type":"text","text":"open"}]},{"type":"text","text":" 的參數中包含文件的路徑名和文件名。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 "},{"type":"codeinline","content":[{"type":"text","text":"write"}]},{"type":"text","text":" 寫數據,其中 "},{"type":"codeinline","content":[{"type":"text","text":"write"}]},{"type":"text","text":" 使用 "},{"type":"codeinline","content":[{"type":"text","text":"open"}]},{"type":"text","text":" 所返回的"},{"type":"text","marks":[{"type":"strong"}],"text":"文件描述符"},{"type":"text","text":",並不使用文件名作爲參數。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用完文件後,要用 "},{"type":"codeinline","content":[{"type":"text","text":"close"}]},{"type":"text","text":" 系統調用關閉文件,避免資源的泄露。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們打開了一個文件後,操作系統會跟蹤進程打開的所有文件,所謂的跟蹤呢,就是操作系統爲每個進程維護一個打開文件表,文件表裏的每一項代表「"},{"type":"text","marks":[{"type":"strong"}],"text":"文件描述符"},{"type":"text","text":"」,所以說文件描述符是打開文件的標識。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4d/4d1f24d7102933f32a560a1cbdfce6da.png","alt":"打開文件表","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"操作系統在打開文件表中維護着打開文件的狀態和信息:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件指針:系統跟蹤上次讀寫位置作爲當前文件位置指針,這種指針對打開文件的某個進程來說是唯一的;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件打開計數器:文件關閉時,操作系統必須重用其打開文件表條目,否則表內空間不夠用。因爲多個進程可能打開同一個文件,所以系統在刪除打開文件條目之前,必須等待最後一個進程關閉文件,該計數器跟蹤打開和關閉的數量,當該計數爲 0 時,系統關閉文件,刪除該條目;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件磁盤位置:絕大多數文件操作都要求系統修改文件數據,該信息保存在內存中,以免每個操作都從磁盤中讀取;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"訪問權限:每個進程打開文件都需要有一個訪問模式(創建、只讀、讀寫、添加等),該信息保存在進程的打開文件表中,以便操作系統能允許或拒絕之後的 I/O 請求;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在用戶視角里,文件就是一個持久化的數據結構,但操作系統並不會關心你想存在磁盤上的任何的數據結構,操作系統的視角是如何把文件數據和磁盤塊對應起來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,用戶和操作系統對文件的讀寫操作是有差異的,用戶習慣以字節的方式讀寫文件,而操作系統則是以數據塊來讀寫文件,那屏蔽掉這種差異的工作就是文件系統了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來分別看一下,讀文件和寫文件的過程:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當用戶進程從文件讀取 1 個字節大小的數據時,文件系統則需要獲取字節所在的數據塊,再返回數據塊對應的用戶進程所需的數據部分。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當用戶進程把 1 個字節大小的數據寫進文件時,文件系統則找到需要寫入數據的數據塊的位置,然後修改數據塊中對應的部分,最後再把數據塊寫回磁盤。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以說,"},{"type":"text","marks":[{"type":"strong"}],"text":"文件系統的基本操作單位是數據塊"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"文件的存儲"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件的數據是要存儲在硬盤上面的,數據在磁盤上的存放方式,就像程序在內存中存放的方式那樣,有以下兩種:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"連續空間存放方式"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"非連續空間存放方式"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中,非連續空間存放方式又可以分爲「鏈表方式」和「索引方式」。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不同的存儲方式,有各自的特點,重點是要分析它們的存儲效率和讀寫性能,接下來分別對每種存儲方式說一下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"連續空間存放方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"連續空間存放方式顧名思義,"},{"type":"text","marks":[{"type":"strong"}],"text":"文件存放在磁盤「連續的」物理空間中"},{"type":"text","text":"。這種模式下,文件的數據都是緊密相連,"},{"type":"text","marks":[{"type":"strong"}],"text":"讀寫效率很高"},{"type":"text","text":",因爲一次磁盤尋道就可以讀出整個文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用連續存放的方式有一個前提,必須先知道一個文件的大小,這樣文件系統纔會根據文件的大小在磁盤上找到一塊連續的空間分配給文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,"},{"type":"text","marks":[{"type":"strong"}],"text":"文件頭裏需要指定「起始塊的位置」和「長度」"},{"type":"text","text":",有了這兩個信息就可以很好的表示文件存放方式是一塊連續的磁盤空間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,此處說的文件頭,就類似於 Linux 的 inode。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7f/7fc9cbf9c253d0ee84af8f61c605d50f.png","alt":"連續空間存放方式","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"連續空間存放的方式雖然讀寫效率高,"},{"type":"text","marks":[{"type":"strong"}],"text":"但是有「磁盤空間碎片」和「文件長度不易擴展」的缺陷。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下圖,如果文件 B 被刪除,磁盤上就留下一塊空缺,這時,如果新來的文件小於其中的一個空缺,我們就可以將其放在相應空缺裏。但如果該文件的大小大於所有的空缺,但卻小於空缺大小之和,則雖然磁盤上有足夠的空缺,但該文件還是不能存放。當然了,我們可以通過將現有文件進行挪動來騰出空間以容納新的文件,但是這個在磁盤挪動文件是非常耗時,所以這種方式不太現實。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5e/5e0b69486a4e42554941c93a44d538e0.png","alt":"磁盤碎片","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外一個缺陷是文件長度擴展不方便,例如上圖中的文件 A 要想擴大一下,需要更多的磁盤空間,唯一的辦法就只能是挪動的方式,前面也說了,這種方式效率是非常低的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼有沒有更好的方式來解決上面的問題呢?答案當然有,既然連續空間存放的方式不太行,那麼我們就改變存放的方式,使用非連續空間存放方式來解決這些缺陷。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"非連續空間存放方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"非連續空間存放方式分爲「鏈表方式」和「索引方式」。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先來看看鏈表的方式。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"鏈表的方式存放是"},{"type":"text","marks":[{"type":"strong"}],"text":"離散的,不用連續的"},{"type":"text","text":",於是就可以"},{"type":"text","marks":[{"type":"strong"}],"text":"消除磁盤碎片"},{"type":"text","text":",可大大提高磁盤空間的利用率,同時"},{"type":"text","marks":[{"type":"strong"}],"text":"文件的長度可以動態擴展"},{"type":"text","text":"。根據實現的方式的不同,鏈表可分爲「"},{"type":"text","marks":[{"type":"strong"}],"text":"隱式鏈表"},{"type":"text","text":"」和「"},{"type":"text","marks":[{"type":"strong"}],"text":"顯式鏈接"},{"type":"text","text":"」兩種形式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件要以「"},{"type":"text","marks":[{"type":"strong"}],"text":"隱式鏈表"},{"type":"text","text":"」的方式存放的話,"},{"type":"text","marks":[{"type":"strong"}],"text":"實現的方式是文件頭要包含「第一塊」和「最後一塊」的位置,並且每個數據塊裏面留出一個指針空間,用來存放下一個數據塊的位置"},{"type":"text","text":",這樣一個數據塊連着一個數據塊,從鏈頭開是就可以順着指針找到所有的數據塊,所以存放的方式可以是不連續的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b3/b3d581a3d561f3386a497d5622180c81.png","alt":"隱式鏈表","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隱式鏈表的存放方式的"},{"type":"text","marks":[{"type":"strong"}],"text":"缺點在於無法直接訪問數據塊,只能通過指針順序訪問文件,以及數據塊指針消耗了一定的存儲空間"},{"type":"text","text":"。隱式鏈接分配的"},{"type":"text","marks":[{"type":"strong"}],"text":"穩定性較差"},{"type":"text","text":",系統在運行過程中由於軟件或者硬件錯誤"},{"type":"text","marks":[{"type":"strong"}],"text":"導致鏈表中的指針丟失或損壞,會導致文件數據的丟失。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果取出每個磁盤塊的指針,把它放在內存的一個表中,就可以解決上述隱式鏈表的兩個不足。那麼,這種實現方式是「"},{"type":"text","marks":[{"type":"strong"}],"text":"顯式鏈接"},{"type":"text","text":"」,它指"},{"type":"text","marks":[{"type":"strong"}],"text":"把用於鏈接文件各數據塊的指針,顯式地存放在內存的一張鏈接表中"},{"type":"text","text":",該表在整個磁盤僅設置一張,"},{"type":"text","marks":[{"type":"strong"}],"text":"每個表項中存放鏈接指針,指向下一個數據塊號"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於顯式鏈接的工作方式,我們舉個例子,文件 A 依次使用了磁盤塊 4、7、2、10 和 12 ,文件 B 依次使用了磁盤塊 6、3、11 和 14 。利用下圖中的表,可以從第 4 塊開始,順着鏈走到最後,找到文件 A 的全部磁盤塊。同樣,從第 6 塊開始,順着鏈走到最後,也能夠找出文件 B 的全部磁盤塊。最後,這兩個鏈都以一個不屬於有效磁盤編號的特殊標記(如 -1 )結束。內存中的這樣一個表格稱爲*"},{"type":"text","marks":[{"type":"italic"}],"text":"文件分配表("},{"type":"text","text":"File Allocation Table,FAT"},{"type":"text","marks":[{"type":"italic"}],"text":")"},{"type":"text","text":"*。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/84/848b55299135f1775cb9fa5e6f9f9272.png","alt":"顯式鏈接","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於查找記錄的過程是在內存中進行的,因而不僅顯著地"},{"type":"text","marks":[{"type":"strong"}],"text":"提高了檢索速度"},{"type":"text","text":",而且"},{"type":"text","marks":[{"type":"strong"}],"text":"大大減少了訪問磁盤的次數"},{"type":"text","text":"。但也正是整個表都存放在內存中的關係,它的主要的缺點是"},{"type":"text","marks":[{"type":"strong"}],"text":"不適用於大磁盤"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如,對於 200GB 的磁盤和 1KB 大小的塊,這張表需要有 2 億項,每一項對應於這 2 億個磁盤塊中的一個塊,每項如果需要 4 個字節,那這張表要佔用 800MB 內存,很顯然 FAT 方案對於大磁盤而言不太合適。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,我們來看看索引的方式。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"鏈表的方式解決了連續分配的磁盤碎片和文件動態擴展的問題,但是不能有效支持直接訪問(FAT除外),索引的方式可以解決這個問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"索引的實現是爲每個文件創建一個「"},{"type":"text","marks":[{"type":"strong"}],"text":"索引數據塊"},{"type":"text","text":"」,裏面存放的是"},{"type":"text","marks":[{"type":"strong"}],"text":"指向文件數據塊的指針列表"},{"type":"text","text":",說白了就像書的目錄一樣,要找哪個章節的內容,看目錄查就可以。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,"},{"type":"text","marks":[{"type":"strong"}],"text":"文件頭需要包含指向「索引數據塊」的指針"},{"type":"text","text":",這樣就可以通過文件頭知道索引數據塊的位置,再通過索引數據塊裏的索引信息找到對應的數據塊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建文件時,索引塊的所有指針都設爲空。當首次寫入第 i 塊時,先從空閒空間中取得一個塊,再將其地址寫到索引塊的第 i 個條目。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1e/1e6d2ef6d8782117779143843798b79e.png","alt":"索引的方式","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"索引的方式優點在於:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件的創建、增大、縮小很方便;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不會有碎片的問題;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持順序讀寫和隨機讀寫;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於索引數據也是存放在磁盤塊的,如果文件很小,明明只需一塊就可以存放的下,但還是需要額外分配一塊來存放索引數據,所以缺陷之一就是存儲索引帶來的開銷。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果文件很大,大到一個索引數據塊放不下索引信息,這時又要如何處理大文件的存放呢?我們可以通過組合的方式,來處理大文件的存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先來看看鏈表 + 索引的組合,這種組合稱爲「"},{"type":"text","marks":[{"type":"strong"}],"text":"鏈式索引塊"},{"type":"text","text":"」,它的實現方式是"},{"type":"text","marks":[{"type":"strong"}],"text":"在索引數據塊留出一個存放下一個索引數據塊的指針"},{"type":"text","text":",於是當一個索引數據塊的索引信息用完了,就可以通過指針的方式,找到下一個索引數據塊的信息。那這種方式也會出現前面提到的鏈表方式的問題,萬一某個指針損壞了,後面的數據也就會無法讀取了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ed/ede534663e1c19169eb40cbe01384da2.png","alt":"鏈式索引塊","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還有另外一種組合方式是索引 + 索引的方式,這種組合稱爲「"},{"type":"text","marks":[{"type":"strong"}],"text":"多級索引塊"},{"type":"text","text":"」,實現方式是"},{"type":"text","marks":[{"type":"strong"}],"text":"通過一個索引塊來存放多個索引數據塊"},{"type":"text","text":",一層套一層索引,像極了俄羅斯套娃是吧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fb/fb7d558d7b1d2d48fa4544adbead57bc.png","alt":"多級索引塊","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Unix 文件的實現方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先把前面提到的文件實現方式,做個比較:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/45/455586f83544d090f64d0e403c34b9c6.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那早期 Unix 文件系統是組合了前面的文件存放方式的優點,如下圖:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0c/0c16919bcad17d1391f162e0363229b7.png","alt":"早期 Unix 文件系統","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它是根據文件的大小,存放的方式會有所變化:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果存放文件所需的數據塊小於 10 塊,則採用直接查找的方式;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果存放文件所需的數據塊超過 10 塊,則採用一級間接索引方式;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果前面兩種方式都不夠存放大文件,則採用二級間接索引方式;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果二級間接索引也不夠存放大文件,這採用三級間接索引方式;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼,文件頭("},{"type":"text","marks":[{"type":"italic"}],"text":"Inode"},{"type":"text","text":")就需要包含 13 個指針:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"10 個指向數據塊的指針;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第 11 個指向索引塊的指針;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第 12 個指向二級索引塊的指針;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第 13 個指向三級索引塊的指針;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,這種方式能很靈活地支持小文件和大文件的存放:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於小文件使用直接查找的方式可減少索引數據塊的開銷;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於大文件則以多級索引的方式來支持,所以大文件在訪問數據塊時需要大量查詢;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個方案就用在了 Linux Ext 2/3 文件系統裏,雖然解決大文件的存儲,但是對於大文件的訪問,需要大量的查詢,效率比較低。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了解決這個問題,Ext 4 做了一定的改變,具體怎麼解決的,本文就不展開了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"空閒空間管理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面說到的文件的存儲是針對已經被佔用的數據塊組織和管理,接下來的問題是,如果我要保存一個數據塊,我應該放在硬盤上的哪個位置呢?難道需要將所有的塊掃描一遍,找個空的地方隨便放嗎?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那這種方式效率就太低了,所以針對磁盤的空閒空間也是要引入管理的機制,接下來介紹幾種常見的方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"空閒表法"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"空閒鏈表法"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"位圖法"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"空閒表法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"空閒表法就是爲所有空閒空間建立一張表,表內容包括空閒區的第一個塊號和該空閒區的塊個數,注意,這個方式是連續分配的。如下圖:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1e/1ec1e7e0b313a92e0028691ae97c9766.png","alt":"空閒表法","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當請求分配磁盤空間時,系統依次掃描空閒表裏的內容,直到找到一個合適的空閒區域爲止。當用戶撤銷一個文件時,系統回收文件空間。這時,也需順序掃描空閒表,尋找一個空閒表條目並將釋放空間的第一個物理塊號及它佔用的塊數填到這個條目中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種方法僅當有少量的空閒區時纔有較好的效果。因爲,如果存儲空間中有着大量的小的空閒區,則空閒表變得很大,這樣查詢效率會很低。另外,這種分配技術適用於建立連續文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"空閒鏈表法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們也可以使用「鏈表」的方式來管理空閒空間,每一個空閒塊裏有一個指針指向下一個空閒塊,這樣也能很方便的找到空閒塊並管理起來。如下圖:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a1/a14c65a4ec2fbb470fba728b818a6d22.png","alt":"空閒鏈表法","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當創建文件需要一塊或幾塊時,就從鏈頭上依次取下一塊或幾塊。反之,當回收空間時,把這些空閒塊依次接到鏈頭上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種技術只要在主存中保存一個指針,令它指向第一個空閒塊。其特點是簡單,但不能隨機訪問,工作效率低,因爲每當在鏈上增加或移動空閒塊時需要做很多 I/O 操作,同時數據塊的指針消耗了一定的存儲空間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"空閒表法和空閒鏈表法都不適合用於大型文件系統,因爲這會使空閒表或空閒鏈表太大。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"位圖法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"位圖是利用二進制的一位來表示磁盤中一個盤塊的使用情況,磁盤上所有的盤塊都有一個二進制位與之對應。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當值爲 0 時,表示對應的盤塊空閒,值爲 1 時,表示對應的盤塊已分配。它形式如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"1111110011111110001110110111111100111 ..."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Linux 文件系統就採用了位圖的方式來管理空閒空間,不僅用於數據空閒塊的管理,還用於 inode 空閒塊的管理,因爲 inode 也是存儲在磁盤的,自然也要有對其管理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"文件系統的結構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面提到 Linux 是用位圖的方式管理空閒空間,用戶在創建一個新文件時,Linux 內核會通過 inode 的位圖找到空閒可用的 inode,並進行分配。要存儲數據時,會通過塊的位圖找到空閒的塊,並分配,但仔細計算一下還是有問題的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數據塊的位圖是放在磁盤塊裏的,假設是放在一個塊裏,一個塊 4K,每位表示一個數據塊,共可以表示 "},{"type":"codeinline","content":[{"type":"text","text":"4 * 1024 * 8 = 2^15"}]},{"type":"text","text":" 個空閒塊,由於 1 個數據塊是 4K 大小,那麼最大可以表示的空間爲 "},{"type":"codeinline","content":[{"type":"text","text":"2^15 * 4 * 1024 = 2^27"}]},{"type":"text","text":" 個 byte,也就是 128M。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也就是說按照上面的結構,如果採用「一個塊的位圖 + 一系列的塊」,外加「一個塊的 inode 的位圖 + 一系列的 inode 的結構」能表示的最大空間也就 128M,這太少了,現在很多文件都比這個大。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Linux 文件系統,把這個結構稱爲一個"},{"type":"text","marks":[{"type":"strong"}],"text":"塊組"},{"type":"text","text":",那麼有 N 多的塊組,就能夠表示 N 大的文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下圖給出了 Linux Ext2 整個文件系統的結構和塊組的內容,文件系統都由大量塊組組成,在硬盤上相繼排布:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d9/d9e36f97b24b2a7f760a81d0b095c36d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最前面的第一個塊是引導塊,在系統啓動時用於啓用引導,接着後面就是一個一個連續的塊組了,塊組的內容如下:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"超級塊"},{"type":"text","text":",包含的是文件系統的重要信息,比如 inode 總個數、塊總個數、每個塊組的 inode 個數、每個塊組的塊個數等等。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"塊組描述符"},{"type":"text","text":",包含文件系統中各個塊組的狀態,比如塊組中空閒塊和 inode 的數目等,每個塊組都包含了文件系統中「所有塊組的組描述符信息」。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"數據位圖和 inode 位圖"},{"type":"text","text":", 用於表示對應的數據塊或 inode 是空閒的,還是被使用中。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"inode 列表"},{"type":"text","text":",包含了塊組中所有的 inode,inode 用於保存文件系統中與各個文件和目錄相關的所有元數據。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"數據塊"},{"type":"text","text":",包含文件的有用數據。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以會發現每個塊組裏有很多重複的信息,比如"},{"type":"text","marks":[{"type":"strong"}],"text":"超級塊和塊組描述符表,這兩個都是全局信息,而且非常的重要"},{"type":"text","text":",這麼做是有兩個原因:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果系統崩潰破壞了超級塊或塊組描述符,有關文件系統結構和內容的所有信息都會丟失。如果有冗餘的副本,該信息是可能恢復的。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過使文件和管理數據儘可能接近,減少了磁頭尋道和旋轉,這可以提高文件系統的性能。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過,Ext2 的後續版本採用了稀疏技術。該做法是,超級塊和塊組描述符表不再存儲到文件系統的每個塊組中,而是隻寫入到塊組 0、塊組 1 和其他 ID 可以表示爲 3、 5、7 的冪的塊組中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"目錄的存儲"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在前面,我們知道了一個普通文件是如何存儲的,但還有一個特殊的文件,經常用到的目錄,它是如何保存的呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於 Linux 一切皆文件的設計思想,目錄其實也是個文件,你甚至可以通過 "},{"type":"codeinline","content":[{"type":"text","text":"vim"}]},{"type":"text","text":" 打開它,它也有 inode,inode 裏面也是指向一些塊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"和普通文件不同的是,"},{"type":"text","marks":[{"type":"strong"}],"text":"普通文件的塊裏面保存的是文件數據,而目錄文件的塊裏面保存的是目錄裏面一項一項的文件信息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在目錄文件的塊中,最簡單的保存格式就是"},{"type":"text","marks":[{"type":"strong"}],"text":"列表"},{"type":"text","text":",就是一項一項地將目錄下的文件信息(如文件名、文件 inode、文件類型等)列在表裏。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"列表中每一項就代表該目錄下的文件的文件名和對應的 inode,通過這個 inode,就可以找到真正的文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/89/89f1e2f2d2a2cb232920d2df16a5f3f4.png","alt":"目錄格式哈希表","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常,第一項是「"},{"type":"codeinline","content":[{"type":"text","text":"."}]},{"type":"text","text":"」,表示當前目錄,第二項是「"},{"type":"codeinline","content":[{"type":"text","text":".."}]},{"type":"text","text":"」,表示上一級目錄,接下來就是一項一項的文件名和 inode。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果一個目錄有超級多的文件,我們要想在這個目錄下找文件,按照列表一項一項的找,效率就不高了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"於是,保存目錄的格式改成"},{"type":"text","marks":[{"type":"strong"}],"text":"哈希表"},{"type":"text","text":",對文件名進行哈希計算,把哈希值保存起來,如果我們要查找一個目錄下面的文件名,可以通過名稱取哈希。如果哈希能夠匹配上,就說明這個文件的信息在相應的塊裏面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Linux 系統的 ext 文件系統就是採用了哈希表,來保存目錄的內容,這種方法的優點是查找非常迅速,插入和刪除也較簡單,不過需要一些預備措施來避免哈希衝突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目錄查詢是通過在磁盤上反覆搜索完成,需要不斷地進行 I/O 操作,開銷較大。所以,爲了減少 I/O 操作,把當前使用的文件目錄緩存在內存,以後要使用該文件時只要在內存中操作,從而降低了磁盤操作次數,提高了文件系統的訪問速度。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"軟鏈接和硬鏈接"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有時候我們希望給某個文件取個別名,那麼在 Linux 中可以通過*"},{"type":"text","marks":[{"type":"italic"}],"text":"硬鏈接("},{"type":"text","text":"Hard Link*)"},{"type":"text","marks":[{"type":"strong"}],"text":" 和"},{"type":"text","text":"軟鏈接("},{"type":"text","marks":[{"type":"italic"}],"text":"Symbolic Link"},{"type":"text","text":")** 的方式來實現,它們都是比較特殊的文件,但是實現方式也是不相同的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"硬鏈接是"},{"type":"text","marks":[{"type":"strong"}],"text":"多個目錄項中的「索引節點」指向一個文件"},{"type":"text","text":",也就是指向同一個 inode,但是 inode 是不可能跨越文件系統的,每個文件系統都有各自的 inode 數據結構和列表,所以"},{"type":"text","marks":[{"type":"strong"}],"text":"硬鏈接是不可用於跨文件系統的"},{"type":"text","text":"。由於多個目錄項都是指向一個 inode,那麼"},{"type":"text","marks":[{"type":"strong"}],"text":"只有刪除文件的所有硬鏈接以及源文件時,系統纔會徹底刪除該文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7c/7cf8cec396a66dca2dd1fcd6dadab537.png","alt":"硬鏈接","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟鏈接相當於重新創建一個文件,這個文件有"},{"type":"text","marks":[{"type":"strong"}],"text":"獨立的 inode"},{"type":"text","text":",但是這個"},{"type":"text","marks":[{"type":"strong"}],"text":"文件的內容是另外一個文件的路徑"},{"type":"text","text":",所以訪問軟鏈接的時候,實際上相當於訪問到了另外一個文件,所以"},{"type":"text","marks":[{"type":"strong"}],"text":"軟鏈接是可以跨文件系統的"},{"type":"text","text":",甚至"},{"type":"text","marks":[{"type":"strong"}],"text":"目標文件被刪除了,鏈接文件還是在的,只不過指向的文件找不到了而已。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/56/5606413e1b5d2c727399711d347617a2.png","alt":"軟鏈接","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"遲到理由"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是的,小林依然遲到了,因爲最近發生了一件非常倒黴的事情,我之前使用的圖牀掛掉了……"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這就導致我所有文章的圖片都掛了,好在大部分博客平臺都會轉存圖片,所以微信公衆號、CSDN、知乎等平臺都正常,但我的本地文章筆記和博客園平臺的圖片都掛掉了,在博客園還有個讀者私信提醒我的文章圖片掛了,他很喜歡小林文章,希望早點恢圖片,太感動了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d5/d5e94f0f35b1bce89ae1e5ed3ff63111.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這就是白嫖免費圖牀的下場,本打算換阿里雲圖牀,但阿里雲圖牀是按訪問流量收費的,如果有人搞你,那直接刷爆你的錢包,想想都可怕,小林窮搞不起搞不起。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後來,詢問了一位朋友 guide 哥,他說可以使用 GitHub 作爲圖牀,用開源工具 Picgo 關聯 GitHub 上傳圖片,再通過 jsdelivr CDN 加速訪問,這一套組合很完美,於是我就採用了此方案搭建了自己的圖牀,依舊繼續白嫖,我就不信 GitHub 也掛!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖牀雖然搞定了,最糟糕的事情纔開始,我要把以前近 "},{"type":"codeinline","content":[{"type":"text","text":"500"}]},{"type":"text","text":" 張的圖片重新保存(以前有的圖片丟了)和分類,並一個一個上傳到 Github,接着還得把圖片的新地址改到本地文章,這工作量簡直要命,到現在我也才搞定了操作系統篇的圖片,網絡篇的圖片還有 2/3 沒弄完,瞬間後悔自己畫那麼多圖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"唉,發完這篇文章,小林還得繼續恢復圖片……"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近,我都在 B 站學習操作系統,但有時候是想看操作系統,但奈何 B 站首頁推送太豐富,看着看着半天就過去了,甚至還花了一天時間專門看一個 UP 主解說「火影忍者」動漫全集,於是就這麼忘了文章的事情,哈哈哈。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/af58580289cd79f55513e25ca4e2d55d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過,確實很過癮,"},{"type":"text","marks":[{"type":"strong"}],"text":"畢竟偷的了忙中閒,方能人上人嘛。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好了,"},{"type":"text","marks":[{"type":"strong"}],"text":"小林是專爲大家圖解的工具人,我們下次見!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":" 好文推薦"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/MnIcTR0KKpgnSoA3xaPUSA","title":""},"content":[{"type":"text","text":"涼了!張三同學沒答好「進程間通信」,被面試官掛了...."}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/yPH0kK0aVM43oNj7vukOCQ","title":""},"content":[{"type":"text","text":"萬粉福利,300 頁圖解網絡 PDF 打包送你"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章