對Linux中文件系統的相關知識進行整理:html
1 總體概況前端
文件系統是應用程序與塊設備(磁盤等)之間的橋樑,是對文件進行統一管理的中間層。對上向上層用戶提供讀寫文件的操做接口,對下將文件在磁盤上進行存儲及有效的管理。對上圖由下往上:node
1)最底層爲塊設備,即存儲硬盤,如PATA, SATA和AHCI等;linux
2)不一樣硬盤對應不一樣的驅動模塊,在Linux系統中,對不一樣硬盤所提供的驅動模塊通常都存放在內核目錄樹drivers/ata中,對於通常通用的硬盤驅動,也許會直接被編譯到內核中;算法
3)通用塊設備層,不一樣的硬盤驅動會提供不一樣的IO接口,爲方便管理,內核把這些接口抽象造成一個統一的對外接口,這樣任意硬盤驅動對外所提供的IO接口都一視同仁的被看做塊設備來處理;緩存
4)文件系統層,對應到具體格式化硬盤的真實文件系統,每一個文件系統實現導出一組通用接口供 VFS 使用。緩衝區緩存會緩存文件系統和相關塊設備之間的請求。例如對底層設備驅動程序的讀寫請求會經過緩衝區緩存來傳遞。這就容許在其中緩存請求,減小訪問物理設備的次數,加快訪問速度;安全
5)虛擬文件系統層,一樣地,不一樣的文件系統對應的結構和操做函數存在差別,VFS就把這些不一樣的文件系統作一個抽象,提供統一的API訪問接口,這樣用戶空間就不用關心不一樣文件系統中不同的API了。Vfs中還有兩個針對文件系統對象的緩存(inode 和 dentry)。它們緩存最近使用過的文件系統對象,減小從硬盤讀取的次數;網絡
6)系統調用層,提供統一的API訪問接口給用戶。數據結構
2 硬盤存儲(瞭解)架構
現代計算機大部分文件存儲功能都是由機械硬盤這種設備提供的。(如今的SSD和閃存從概念和邏輯上都部分繼承自機械硬盤,因此使用機械硬盤來進行理解也是沒有問題的)機械硬盤能實現信息存儲的功能基於:磁性存儲介質可以被磁化,且磁化後會長久保留被磁化的狀態,這種被磁化狀態可以被讀取出來,同時這種磁化狀態還可以不斷被修改,磁化正好有兩個方向,因此能夠表示0和1。因而硬盤就是把這種磁性存儲介質作成一個個盤片,每個盤片上都分佈着數量巨大的磁性存儲單位,使用磁性讀寫頭對盤片進行寫入和讀取。
磁頭讀寫文件的時候,首先是分區讀寫的,由inode編號找到對應的磁道和扇區,而後一個柱面一個柱面地進行讀寫。機械硬盤的讀寫控制系統是一個使人歎爲觀止的精密工程(一個盤面上有幾億個存儲單位,每一個磁道寬度不到幾十納米,磁盤每分鐘上萬轉),同時關於讀寫的邏輯也是有諸多細節(好比扇區的編號並非連續的)等等。
有了硬盤並不意味着LInux能夠馬上把它用來存儲,還須要組合進Linux的文件體系才能被Linux使用。
3 真實文件系統,以ext2爲例
1、硬盤分區
硬盤分區是硬盤結合到文件體系的第一步,本質是「硬盤」這個物理概念轉換成「區」這個邏輯概念,爲下一步格式化作準備。分區自己並非必須的,能夠把一整塊硬盤做爲一個區。但從數據的安全性以及系統性能角度來看,分區仍是有不少用處的,因此通常都會對硬盤進行分區。
每塊硬盤上最重要的第一扇區中有硬盤主引導記錄(Master boot record, MBR) 及分區表(partition table), 其中 MBR 佔有 446 bytes,而分區表佔有 64 bytes。硬盤主引導記錄放有最基本的引導加載程序,是系統開機啓動的關鍵環節。而分區表則跟分區有關,它記錄了硬盤分區的相關信息,但因分區表僅有 64bytes , 因此最多隻能記彔四塊分區(分區自己其實就是對分區表進行設置)。
只能分四個區實在太少了,因而就有了擴展分區的概念,既然第一個扇區所在的分區表只能記錄四條數據, 那可利用額外的扇區來記錄更多的分區信息。
把普通能夠訪問的分區稱爲主分區,擴展分區不一樣於主分區,它自己並無內容,它是爲進一步邏輯分區提供空間的。在某塊分區指定爲擴展分區後,就能夠對這塊擴展分區進一步分紅多個邏輯分區。操做系統規定:
四塊分區每塊均可以是主分區或擴展分區
擴展分區最多隻能有一個(也不必有多個)
擴展分區能夠進一步分割爲多個邏輯分區
擴展分區只是邏輯概念,自己不能被訪問,也就是不能被格式化後做爲數據訪問的分區,可以做爲數據訪問的分區只有主分區和邏輯分區
邏輯分區的數量依操做系統而不一樣,通常給硬盤進行分區時,一個主分區一個擴展分區,而後把擴展分區劃分爲N個邏輯分區是最好的。特殊的,最好單獨分一個swap區(內存置換空間),它獨爲一類,功能是:當有數據被存放在物理內存裏面,可是這些數據又不是常被 CPU 所取用時,那麼這些不常被使用的程序將會被丟到硬盤的 swap 置換空間當中, 而將速度較快的物理內存空間釋放出來給真正須要的程序使用。
2、文件系統格式化
Linux操做系統支持不少不一樣的文件系統,好比ext二、ext三、XFS、FAT等等,而Linux把對不一樣文件系統的訪問交給了VFS(虛擬文件系統),VFS能訪問和管理各類不一樣的文件系統。因此有了區以後就須要把它格式化成具體的文件系統以便VFS訪問。
標準的Linux文件系統Ext2是使用「基於inode的文件系統」。通常操做系統的文件數據除了文件實際內容外, 還帶有不少屬性,例如 Linux 操做系統的文件權限(rwx)與文件屬性(擁有者、羣組、 時間參數等),文件系統一般會將屬性和實際內容這兩部分數據分別存放在不一樣的區塊。在基於inode的文件系統中,權限與屬性放置到 inode 中,實際數據放到 data block 區塊中,並且inode和data block都有編號。
Ext2 文件系統在此基礎上:
1)文件系統最前面有一個啓動扇區(boot sector)。這個啓動扇區能夠安裝開機管理程序, 這個設計讓咱們能將不一樣的引導裝載程序安裝到個別的文件系統前端,而不用覆蓋整個硬盤惟一的MBR, 也就是這樣才能實現多重引導的功能;
2)把每一個區進一步分爲多個塊組 (block group),每一個塊組有獨立的inode/block體系。若是文件系統高達數百 GB 時,把全部的 inode 和block 統統放在一塊兒會由於 inode 和 block的數量太龐大,不容易管理。每一個塊組實際還會分爲分爲6個部分,除了inode table 和 data block外還有4個附屬模塊,起到優化和完善系統性能的做用。
故ext2文件系統的硬盤佈局大體以下:
如上圖,引導啓動塊(能夠沒有,有的話一包在起始處)以後存儲了文件系統的元數據和各個文件有用的數據。超級塊是用於存儲文件系統自身元數據的核心結構,能夠看到每一個塊組中都有一個超級塊,信息是冗餘的,可是這麼作的緣由是:
1) 若是系統崩潰破壞了超級塊,有關文件系統結構和內容的全部信息都會丟失。若是有冗餘的副本,該信息是可能恢復的雖然難度極高;
2) 經過使文件和管理數據儘量接近,減小了磁頭尋道和旋轉,能夠提升文件系統的性能。
可是實際上,數據並不是在每一個塊組中都複製,內核通常只用超級塊的第一個副本工做。在進行文件系統檢查時,會將第一個超級塊的數據傳播到剩餘的超級塊,供緊急狀況下讀取。可是這種方式會消耗大量的存儲空間,所以如今通常採用稀疏超級塊技術,即超級塊再也不存儲到文件系統的每一個塊組中,而是隻寫入到塊組0、1和其餘可表示爲三、五、7的冪的塊組中。且超級塊的數據緩存在內存中,使得內核沒必要重複從硬盤中讀取數據。
塊組中各個結構的做用以下:
1)超級塊:用於存儲文件系統自身元數據的核心結構,通常大小爲1024bytes,記錄的信息主要有:block 與inode 的總量;未使用與已使用的inode / block 數量;一個valid bit 數值,若此文件系統已被掛載,則valid bit 爲0 ,若未被掛載,則valid bit 爲1;block 與inode 的大小 (block 爲1, 2, 4K,inode 爲128bytes 或256bytes);其餘各類文件系統相關信息:filesystem 的掛載時間、最近一次寫入資料的時間、最近一次檢驗磁碟(fsck) 的時間,及文件系統類型磨數用於mount;
內核中其對應的數據結構以下:
如上其中s_blocks_count 記錄了硬盤分區上 block 的總數,而 s_blocks_per_group 記錄了每一個 group 中有多少個 block,文件系統上的 block groups 數量假設爲G,則G = (s_blocks_count - s_first_data_block - 1) / s_blocks_per_group + 1。減去 s_first_data_block是由於 s_blocks_count 是硬盤分區上所有的 block 的數量,而在 s_first_data_block 以前的 block 是不歸 block group 管的,因此要減去。最後加一是由於尾巴上可能多出來一些 block,這些 block要把它劃在一個相對較小的 group 裏面。
2)組描述符表:包含的信息反映了文件系統中各個塊組的狀態,如塊組中空閒塊和inode的數目,每一個塊組都包含了文件系統中全部塊組的組描述符信息,數據結構以下:
指針bg_block_bitmap指向這個 block group 的塊位圖block bitmap,bg_inode_bitmap指向 inode bitmap, bg_inode_table則指向inode_table。
3)數據塊位圖和inode位圖(均只有一個塊):用於保存長的比特位串,這些結構中的每一個比特位都對應於一個數據塊或inode,若是位爲0表示有數據,爲1則表示空閒;
4)inode表:包含塊組中全部的inode,inode用於保存文件系統中與各個文件和目錄相關的全部元數據;主要記錄文件的屬性以及該文件實際數據是放置在哪些block中,它記錄的信息至少有:文件大小、文件真正內容的block號碼(一個或多個);訪問模式(read/write/excute);擁有者與羣組(owner/group);各類時間:創建或狀態改變的時間、最近一次的讀取時間、最近修改的時間;注意沒有文件名!文件名在目錄的block中!目錄其實是一個目錄文件,其block中存放的是目錄下的文件名/子目錄名及其對應的inode號。
一個文件佔用一個 inode,每一個inode有編號,Linux 系統存在 inode 號被用完但磁盤空間還有剩餘的狀況。注意這裏的文件不僅僅是普通文件,目錄文件也是一個文件,inode 的數量與大小在格式化時就已經固定了,每一個inode 大小均固定爲128 bytes (新的ext4 與xfs 可設定到256 bytes);文件系統可以創建的文件數量與inode 的數量有關,存在空間還夠但inode不夠的狀況;系統讀取文件時須要先找到inode,並分析inode 所記錄的權限與使用者是否符合,若符合纔可以開始實際讀取 block 的內容,inode結構以下:
經過inode號讀取文件數據的過程:一個硬盤分區上的block 計數是從 0 開始的,而且這個計數對於這個硬盤分區來講是全局性質的,inode 計數同 block 計數同樣,也是全局性質的,inode 計數是從 1 開始。在 super block 中有一個字段 s_inodes_per_group 記載了每一個 block group 中有多少個 inode。用獲得的 inode 號數除以 s_inodes_per_group可知這個 inode 是在哪個 block group 裏面,餘數即爲這個 inode 是這個 block group 裏面的第幾個 inode;而後先找到這個 block group 的 group descriptor,從這個 descriptor找到這個 group 的 inode table,再從 inode table 找到所需的第幾個 inode,以後就能夠開始讀取 inode 中的用戶數據了。即:block_group = (ino - 1) / s_inodes_per_group。這裏 ino 就是獲得的 inode 號數。而 offset = (ino - 1) % s_inodes_per_group,這個 offset 就指出了該inode 是這個 block group 裏面的第幾個 inode。減1由於塊組從0開始。
因爲inode大小有限,記錄一個block 號碼須要4byte ,假設一個文件有400MB 且每一個block 爲4K 時, 那麼至少也要十萬條block 號碼的記錄,那麼須要花費很大的空間來存儲,所以系統將inode 記錄block 號碼的區域定義爲12個直接,一個間接, 一個雙間接與一個三間接記錄區:
由inode可知其可存放 EXT2_N_BLOCKS個 block 指針:
這組 15 個 block 指針的前 12 個是direct blocks,裏面直接存放的就是用戶數據。第 13 個 block爲indirect block,裏面存放的所有是 block 指針,這些 block 指針指向的 block 才被用來存放用戶數據。第 14 個 block 是所謂的 double indirect block,裏面存放的全是 block 指針,這些 block 指針指向的 block 也被所有用來存放 block 指針,而這些 block 指針指向的 block才被用來存放用戶數據。第 15 個 block 是所謂的 triple indirect block,比double indirect block 又多了一層 block 指針,結構以下圖。一個 inode 裏面實際有多少個 block是由 inode 字段 i_size 再經過計算獲得的。i_size 記錄的是文件或者目錄的實際大小,用它的值除以 block 的大小,就能夠得出這個 inode 一共佔有幾個 block。
5)數據塊:包含了文件的真實數據,在格式化時block的大小就固定了,且每一個block都有編號,以方便inode的記錄;原則上,block 的大小與數量在格式化完就不可以再改變了(除非從新格式化);在Ext2文件系統中所支持的block大小有1K, 2K及4K三種,因爲block大小的區別,會致使該文件系統可以支持的最大磁盤容量與最大單一文件容量各不相同,Block 大小爲4KB時最大文件長度爲2TB。每一個block 內最多隻可以放置一個文件的資料,但一個文件能夠放在多個block中(大的話);若文件小於block ,則該block 的剩餘容量就不可以再被使用了(磁盤空間會浪費,帶來碎片),但若是block 較小的話,那麼大型檔案將會佔用數量更多的block ,而inode 也要記錄更多的block 號碼,此時將可能致使檔案系統不良的讀寫效能,所以須要根據狀況決定,通常爲4k.
4 掛載(須要補充)
在一個區被格式化爲一個文件系統以後,它就能夠被Linux操做系統使用了,只是這個時候Linux操做系統還找不到它,因此咱們還須要把這個文件系統「註冊」進Linux操做系統的文件體系裏,這個操做就叫「掛載」 (mount)。掛載是利用一個目錄當成進入點(相似選一個現成的目錄做爲代理),將文件系統放置在該目錄下,也就是說,進入該目錄就能夠讀取該文件系統的內容,相似整個文件系統只是目錄樹的一個文件夾(目錄)。這個進入點的目錄稱爲「掛載點」。因爲整個 Linux 系統最重要的是根目錄,所以根目錄必定須要掛載到某個分區。 而其餘的目錄則可依用戶本身的需求來給予掛載到不一樣的分去。
Linux的文件體系的構建過程總結一下就是:硬盤通過分區和格式化,每一個區都成爲了一個文件系統,掛載這個文件系統後就可讓Linux操做系統經過VFS訪問硬盤時跟訪問一個普通文件夾同樣。
5 虛擬文件系統VFS
文件系統並不僅有上面所說的ext2等,文件系統通常能夠分爲如下幾種:
1) 基於磁盤的文件系統是在非易失性介質上存儲文件的經典方法,如ext2/3等;
2) 虛擬文件系統在內核中生成,是一種使用戶應用程序和用戶通訊的方法,如procfs,sysfs,其不須要在任何種類的硬件設備上分配存儲空間,其內容是從內核數據結構包含的信息生成的。
3) 網絡文件系統是基於磁盤的文件系統和虛擬文件系統之間的折中。數據實際上存儲在一個不一樣系統的硬件設備上,內核無需關注文件存取、數據組織和硬件通訊的細節,這些由遠程計算機的內核處理。
因爲文件系統存在差別,實現及接口也不一致,內核爲了管理使用了VFS虛擬文件系統進行抽象,提供一種操做文件、目錄和其餘對象的統一方法,使用戶看不到底層文件系統的差別。該文件系統只是虛擬存在,必須使用各類對象和函數指針與每種文件系統適配。
VFS 做爲文件系統接口的根層,記錄當前支持的文件系統以及當前掛載的文件系統。可使用一組註冊函數在 Linux 中動態地添加或刪除文件系統。內核保存當前支持的文件系統的列表,能夠經過 /proc 文件系統在用戶空間中查看這個列表。這個虛擬文件還顯示當前與這些文件系統相關聯的設備。在 Linux 中添加新文件系統的方法是調用 register_filesystem()。這個函數的參數定義一個文件系統結構(file_system_type)的引用,這個結構定義文件系統的名稱、一組屬性和兩個超級塊函數。可經過unregistr_filesystem()註銷文件系統。在註冊新的文件系統時,會把這個文件系統和它的相關信息添加到 file_systems 列表中。這個列表定義能夠支持的文件系統。可經過cat /proc/filesystems查看該列表。
Linux VFS 存在四個基本對象:超級塊對象 (superblock object)、索引節點對象 (inode object)、目錄項對象 (dentry object) 及文件對象 (file object)。
超級塊對象表明一個已安裝的文件系統,每一個超級塊實例對應一個掛載的文件系統,若是已經掛載,就是活動超級塊,固然一個超級塊能夠掛載到多個地方,每安裝一個文件系統,就對應有一個超級塊和安裝點。超級塊經過它的一個域s_type指向其對應的具體的文件系統類型。具體的文件系統經過file_system_type中的一個域fs_supers連接具備同一種文件類型的超級塊。同一種文件系統類型的超級塊經過域s_instances連接(所以對於每一個掛載的文件系統都有一個超級塊,一個安裝實例和一個超級塊對象一一對應,同一文件系統的超級塊實例經過哈希鏈表組織);
索引節點對象表明一個文件,索引節點對象存儲了文件的相關信息,表明了存儲設備上的一個實際的物理文件。當一個文件首次被訪問時,內核會在內存中組裝相應的索引節點對象,以便向內核提供一個對文件進行操做時所必須的所有信息;這些信息一部分存儲在磁盤特定位置,另一部分是在加載時動態填充的。inode 表示文件系統中的一個對象,它具備唯一標識符,各個文件系統提供將文件名映射爲唯一 inode 標識符和 inode 引用的方法;
目錄項對象表明一個目錄項,一個路徑的各個組成部分,無論是目錄仍是普通的文件,都是一個目錄項對象,如設備文件 event5 在路徑 /dev/input/event5 中,其存在四個目錄項對象:/ 、dev/ 、input/ 及 event5。爲文件路徑的快速解析,Linux VFS 設計了目錄項緩存(Directory Entry Cache,即 dcache),inode 和目錄緩存分別保存最近使用的 inode 和 dentry,對於 inode 緩存中的每一個 inode,在目錄緩存中都有一個對應的 dentry。
文件對象表明由進程打開的文件,文件對象經過f_inode指向其對應的inode。文件對象和物理文件的關係有點相似進程和程序的關係。由於多個進程能夠同時打開和操做同一個文件,因此同一個文件也可能存在多個對應的文件對象。文件對象僅僅在進程觀點上表明已經打開的文件。一個文件對應的文件對象可能不是唯一的,可是其對應的索引節點和目錄項對象是唯一的。
總體結構概觀以下:圖源
須要清楚VFS是真實文件系統的一個抽象,具體的操做都由真實文件系統實現,VFS使用相關的函數指針指向具體文件系統的函數實現,如super_operations, inode_operations 和 file_operations等。VFS相關數據結構須要使用實際文件系統數據進行填充,如vfs inode 和ext3 inode,以下,系統中的alloc_inode函數:
Ext3_create中調用了ext3_new_inode:
Ext3_new_inode中:
EXT_I爲:return container_of(inode, struct ext3_inode_info, vfs_inode);
可見ext3_inode_info中成員vfs_inode指向對應的vfs inode,而且使用實際的ext3_inode_info填充 vfs inode的相關成員,而vfs inode中使用i_private指向對應的ext3_inode_info, 相似地,vfs中super_block結構中使用s_fs_inof指針指向實際的ext3_sb_info,可見vfs中的相關數據結構和真實文件系統的數據結構是有對應的。
對於vfs的目錄項dentry,每一個由vfs發送到底層實現的請求,都會致使建立一個新的dentry對象以保存請求的結果,這些對象保存在一個緩存中在下一次須要時能夠快速訪問。dentry表示目錄文件,目錄文件block內容有:該目錄項的數據所在inode的編號,文件或目錄的名稱,block中還會自動生成兩條記錄,一條是.文件夾記錄,inode指向自身,另外一條是..文件夾記錄,inode指向父文件夾。以查找/use/bin/emacs文件對應的inode進行說明:第一個爲根目錄/,依次查找獲得最終文件的inode。Dentry結構中d_subdirs鏈表爲給定目錄下的全部文件/子目錄相關聯的dentry實例,d_parent爲父目錄dentry實例,d_name爲目錄/文件名稱,當其很短時存至成員d_iname中。
6 塊io層
塊設備(Block Device)是支持以固定長度的塊爲單位讀寫數據的存儲設備的統稱。Linux內核中負責提交對塊設備IO請求的子系統被稱爲塊IO子系統,也被稱爲Linux塊層,結構如上圖。
1)通用塊層爲各類類型的塊設備創建了一個統一的模型,它主要的工做是接收上層發出的磁盤請求,並最終發出IO請求。該層隱藏了底層硬件塊設備的特性,爲塊設備提供了一個通用的抽象視圖。
2)IO調度層:接收通用塊層發出的IO請求,緩存請求並試圖合併相鄰的請求(若是請求在磁盤上面是相鄰的),並根據設置好的算法,回調驅動層提供的請求處理函數,以處理具體的IO請求。
3)塊設備驅動層:具體的IO處理交給塊設備驅動層來完成,視塊設備的不一樣。對於大多數邏輯塊設備,塊設備驅動多是一個純粹的軟件層,並不須要直接和硬件打交道,只是機械地重定向IO。對於SCSI塊設備,其塊設備驅動即爲SCSI磁盤驅動,爲SCSI子系統的高層驅動,從而將塊IO子系統和SCSI子系統聯繫了起來。
塊IO子系統的通常IO處理流程是:上層調用通用塊層提供的接口向塊IO子系統提交IO請求,這些請求首先被放入IO調度層的調度隊列,通過合併和排序,最終將轉換後的IO請求派發到具體的塊設備的等待隊列,由後者的驅動進一步處理。這個過程涉及兩種形式的IO請求:一種是通用塊層的IO請求,即上層提交的IO請求,在Linux內核中以bio結構描述;另外一種是塊設備驅動層的IO請求,即通過IO調度層轉換後的IO請求,在Linux內核中以request描述。bio表示上層發給通用塊層的請求,稱爲通用塊層請求,它關注的是請求的應用層面,即讀取(或寫入)哪一個塊設備,讀取(或寫入)多少字節的數據,讀取(或寫入)到哪一個目標緩衝區等、request表示通用塊層爲底層塊設備驅動準備的請求,稱做塊設備驅動層IO請求,或塊設備驅動請求,它關注的是請求的實施層面,即構造哪一種類型的SCSI命令。
IO簡單來說,就是將數據從磁盤讀入內存或者從內存寫入磁盤。可是,爲了提高系統性能,塊IO子系統採用了聚散IO(scatter/gather IO)這樣一種機制:將對磁盤上連續,但內存中不連續的的數據訪問由單次操做便可完成。也就是說,在單次操做中,從磁盤上的連續扇區中的數據讀取到幾個物理上不連續的內存空間或者將物理上不連續的內存空間的數據寫入磁盤的連續扇區。前者叫分散讀,後者叫彙集寫。上層向通用塊層提交的IO請求是基於聚散IO的,它包含多個「請求段(segment)」,一個「請求段」是一段連續的內存區域,其中包含了和其餘「請求段」處於連續扇區的數據。
一個塊設備驅動層請求可能包含多個通用塊層請求,也就是說,一次SCSI命令能夠服務多個上層請求,這就是所謂的請求合併。在Linux內核實現中,請求合併就是將多個bio鏈入到同一個request。此外,塊IO子系統還涉及不一樣的請求隊列,包括IO調度隊列和派發隊列。IO調度隊列是塊IO子系統用於對通用塊層請求進行合併和排序的隊列。派發隊列是針對塊設備驅動的,即塊IO子系統嚴格按照隊列順序提交塊設備驅動層請求給塊設備驅動處理。通常來講,每一個塊設備都有一個派發隊列,IO子系統又爲它內部維護了一個IO調度隊列,不一樣的塊設備能夠採用不一樣的IO調度算法。
通用塊層請求到達塊IO子系統時,首先在IO調度隊列中進行合併和排序,變成爲塊設備驅動層的請求。以後塊設備驅動層請求按照特定的算法被轉移到派發隊列,從而被提交到塊設備驅動。在Linux內核中,IO調度隊列和派發隊列都反應在request_queue結構中。
一些問題:
一、 爲何不能對目錄建立硬連接
(1)首先明確硬連接和軟連接的概念:軟連接也稱爲符號連接。連接爲 Linux 系統解決了文件的共享使用,還帶來了隱藏文件路徑、增長權限安全及節省存儲等好處。若一個 inode 號對應多個文件名,則稱這些文件爲硬連接。換言之,硬連接就是同一個文件使用了多個別名。
因爲硬連接是有着相同 inode 號僅文件名不一樣的文件,所以硬連接存在如下幾點特性:文件有相同的 inode 及 data block;只能對已存在的文件進行建立;不能交叉文件系統進行硬連接的建立;不能對目錄進行建立,只可對文件建立;刪除一個硬連接文件並不影響其餘有相同 inode 號的文件。
軟連接與硬連接不一樣,若文件用戶數據塊中存放的內容是另外一文件的路徑名的指向,則該文件就是軟鏈接。軟連接就是一個普通文件,只是數據塊內容有點特殊。軟連接有着本身的 inode 號以及用戶數據塊。所以軟連接的建立與使用沒有相似硬連接的諸多限制:軟連接有本身的文件屬性及權限等;可對不存在的文件或目錄建立軟連接;軟連接可交叉文件系統;軟連接可對文件或目錄建立;建立軟連接時,連接計數 i_nlink 不會增長;刪除軟連接並不影響被指向的文件,但若被指向的原文件被刪除,則相關軟鏈接被稱爲死連接(若被指向路徑文件被從新建立,死連接可恢復爲正常的軟連接)。二者如圖:
(2)爲何不能對目錄建立硬連接
1)若是使用 hard link 連接到目錄時, 連接的數據須要連同被連接目錄底下的全部數據都建立連接,舉例來講,若是你要將 /etc 使用實體連接建立一個 /etc_hd 的目錄時,那麼在 /etc_hd 底下的全部文件/目錄同時都與 /etc 底下的文件/目錄建立 hard link 。 且將來若是須要在 /etc_hd 底下建立新文件時,連帶的 /etc 底下的數據又得要建立一次 hard link ,所以形成環境至關大的複雜度;
2)硬連接的建立會致使當前目錄.和父目錄..指向混亂,具體
3)對目錄的硬連接可能在目錄中引入循環,在遍歷目錄的時候會使系統陷入無限循環。舉例來講,文件夾 a,b,在a下面建立b文件夾的硬連接c,在b下面建立a文件夾的硬連接d,ls a ,會看到c,ls c,看到b下的文件夾d,再ls d又看到c,這樣能夠無限ls下去。
在linux系統中,每一個文件(目錄也是文件)都對應着一個inode結構,其中inode數據結構中包含了文件類型(目錄,普通文件,符號鏈接文件等等)的信息,操做系統在遍歷目錄時能夠判斷出符號連接,既然能夠判斷出符號連接就能夠採起一些措施來防範進入過大的循環,所以系統在連續遇到8個符號鏈接後就中止遍歷,故目錄符號連接不會進入死循環。軟鏈接在訪問時readlink有遞歸次數的限制,硬連接就是普通inode,因此沒辦法記錄遞歸次數。
另:dentry經過文件名和父目錄dentry結構地址進行哈希放入哈希表中,內核中哈希函數的實現
參考《深刻Linux內核架構》及以下連接
總體:https://www.cnblogs.com/bellkosmos/p/detail_of_linux_file_system.html
Ext2: https://www.ibm.com/developerworks/cn/linux/filesystem/ext2/index.html