Linux文件系統是怎麼工做的?

Linux文件系統是怎麼工做的?

本文已收錄GitHub,更有互聯網大廠面試真題,面試攻略,高效學習資料等node

磁盤爲系統提供了最基本的持久化存儲。git

文件系統則在磁盤的基礎上,提供了一個用來管理文件的樹狀結構。github

那麼,磁盤和文件系統是怎麼工做的呢?又有哪些指標能夠衡量它們的性能呢?面試

索引節點和目錄項

文件系統,自己是對存儲設備上的文件,進行組織管理的機制。組織方式不一樣,就會造成不一樣的文件系統。數據庫

你要記住最重要的一點,在 Linux 中一切皆文件。不只普通的文件和目錄,就連塊設備、套接字、管道等,也都要經過統一的文件系統來管理。編程

爲了方便管理,Linux 文件系統爲每一個文件都分配兩個數據結構,索引節點(indexnode)和目錄項(directory entry)。它們主要用來記錄文件的元信息和目錄結構。緩存

索引節點,簡稱爲 inode,用來記錄文件的元數據,好比 inode 編號、文件大小、訪問權限、修改日期、數據的位置等。索引節點和文件一一對應,它跟文件內容同樣,都會被持久化存儲到磁盤中。因此記住,索引節點一樣佔用磁盤空間。服務器

目錄項,簡稱爲 dentry,用來記錄文件的名字、索引節點指針以及與其餘目錄項的關聯關係。多個關聯的目錄項,就構成了文件系統的目錄結構。不過,不一樣於索引節點,目錄項是由內核維護的一個內存數據結構,因此一般也被叫作目錄項緩存。網絡

換句話說,索引節點是每一個文件的惟一標誌,而目錄項維護的正是文件系統的樹狀結構。目錄項和索引節點的關係是多對一,你能夠簡單理解爲,一個文件能夠有多個別名。數據結構

舉個例子,經過硬連接爲文件建立的別名,就會對應不一樣的目錄項,不過這些目錄項本質上仍是連接同一個文件,因此,它們的索引節點相同。

索引節點和目錄項紀錄了文件的元數據,以及文件間的目錄關係,那麼具體來講,文件數據究竟是怎麼存儲的呢?是否是直接寫到磁盤中就行了呢?

實際上,磁盤讀寫的最小單位是扇區,然而扇區只有 512B 大小,若是每次都讀寫這麼小的單位,效率必定很低。因此,文件系統又把連續的扇區組成了邏輯塊,而後每次都以邏輯塊爲最小單元,來管理數據。常見的邏輯塊大小爲 4KB,也就是由連續的 8 個扇區組成。

爲了幫助你理解目錄項、索引節點以及文件數據的關係,我畫了一張示意圖。你能夠對照着這張圖,來回憶剛剛講過的內容,把知識和細節串聯起來。

Linux文件系統是怎麼工做的?

不過,這裏有兩點須要你注意。

第一,目錄項自己就是一個內存緩存,而索引節點則是存儲在磁盤中的數據。在前面的Buffer 和 Cache 原理中,我曾經提到過,爲了協調慢速磁盤與快速 CPU 的性能差別,文件內容會緩存到頁緩存 Cache 中。

那麼,你應該想到,這些索引節點天然也會緩存到內存中,加速文件的訪問。

第二,磁盤在執行文件系統格式化時,會被分紅三個存儲區域,超級塊、索引節點區和數據塊區。其中,

  • 超級塊,存儲整個文件系統的狀態。
  • 索引節點區,用來存儲索引節點。
  • 數據塊區,則用來存儲文件數據。

虛擬文件系統

目錄項、索引節點、邏輯塊以及超級塊,構成了 Linux 文件系統的四大基本要素。不過,爲了支持各類不一樣的文件系統,Linux 內核在用戶進程和文件系統的中間,又引入了一個抽象層,也就是虛擬文件系統 VFS(Virtual File System)。

VFS 定義了一組全部文件系統都支持的數據結構和標準接口。這樣,用戶進程和內核中的其餘子系統,只須要跟 VFS 提供的統一接口進行交互就能夠了,而不須要再關心底層各類文件系統的實現細節。

這裏,我畫了一張 Linux 文件系統的架構圖,幫你更好地理解系統調用、VFS、緩存、文件系統以及塊存儲之間的關係。

Linux文件系統是怎麼工做的?

經過這張圖,你能夠看到,在 VFS 的下方,Linux 支持各類各樣的文件系統,如 Ext四、XFS、NFS 等等。按照存儲位置的不一樣,這些文件系統能夠分爲三類。

  • 第一類是基於磁盤的文件系統,也就是把數據直接存儲在計算機本地掛載的磁盤中。常見的 Ext四、XFS、OverlayFS 等,都是這類文件系統。
  • 第二類是基於內存的文件系統,也就是咱們常說的虛擬文件系統。這類文件系統,不須要任何磁盤分配存儲空間,但會佔用內存。咱們常常用到的 /proc 文件系統,其實就是一種最多見的虛擬文件系統。此外,/sys 文件系統也屬於這一類,主要向用戶空間導出層次化的內核對象。
  • 第三類是網絡文件系統,也就是用來訪問其餘計算機數據的文件系統,好比 NFS、SMB、iSCSI 等。

這些文件系統,要先掛載到 VFS 目錄樹中的某個子目錄(稱爲掛載點),而後才能訪問其中的文件。拿第一類,也就是基於磁盤的文件系統爲例,在安裝系統時,要先掛載一個根目錄(/),在根目錄下再把其餘文件系統(好比其餘的磁盤分區、/proc 文件系統、/sys 文件系統、NFS 等)掛載進來。

文件系統I/O

把文件系統掛載到掛載點後,你就能經過掛載點,再去訪問它管理的文件了。VFS 提供了一組標準的文件訪問接口。這些接口以系統調用的方式,提供給應用程序使用。

就拿 cat 命令來講,它首先調用 open() ,打開一個文件;而後調用 read() ,讀取文件的內容;最後再調用 write(),把文件內容輸出到控制檯的標準輸出中:

int open(const char *pathname, int flags, mode_t mode);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

文件讀寫方式的各類差別,致使 I/O 的分類多種多樣。最多見的有,緩衝與非緩衝 I/O、直接與非直接 I/O、阻塞與非阻塞 I/O、同步與異步 I/O 等。 接下來,咱們就詳細看這四種分類。

第一種,根據是否利用標準庫緩存,能夠把文件 I/O 分爲緩衝 I/O 與非緩衝 I/O。

緩衝 I/O,是指利用標準庫緩存來加速文件的訪問,而標準庫內部再經過系統調度訪問文件。
非緩衝 I/O,是指直接經過系統調用來訪問文件,再也不通過標準庫緩存。

注意,這裏所說的「緩衝」,是指標準庫內部實現的緩存。比方說,你可能見到過,不少程序遇到換行時才真正輸出,而換行前的內容,其實就是被標準庫暫時緩存了起來。

不管緩衝 I/O 仍是非緩衝 I/O,它們最終仍是要通過系統調用來訪問文件。

第二,根據是否利用操做系統的頁緩存,能夠把文件 I/O 分爲直接 I/O 與非直接 I/O。

直接 I/O,是指跳過操做系統的頁緩存,直接跟文件系統交互來訪問文件。
非直接 I/O 正好相反,文件讀寫時,先要通過系統的頁緩存,而後再由內核或額外的系統調用,真正寫入磁盤。

想要實現直接 I/O,須要你在系統調用中,指定 O_DIRECT 標誌。若是沒有設置過,默認的是非直接 I/O。

不過要注意,直接 I/O、非直接 I/O,本質上仍是和文件系統交互。若是是在數據庫等場景中,你還會看到,跳過文件系統讀寫磁盤的狀況,也就是咱們一般所說的裸 I/O。

第三,根據應用程序是否阻塞自身運行,能夠把文件 I/O 分爲阻塞 I/O 和非阻塞 I/O:

所謂阻塞 I/O,是指應用程序執行 I/O 操做後,若是沒有得到響應,就會阻塞當前線程,天然就不能執行其餘任務。
所謂非阻塞 I/O,是指應用程序執行 I/O 操做後,不會阻塞當前的線程,能夠繼續執行其餘的任務,隨後再經過輪詢或者事件通知的形式,獲取調用的結果。

比方說,訪問管道或者網絡套接字時,設置 O_NONBLOCK 標誌,就表示用非阻塞方式訪問;而若是不作任何設置,默認的就是阻塞訪問。

第四,根據是否等待響應結果,能夠把文件 I/O 分爲同步和異步 I/O:

所謂同步 I/O,是指應用程序執行 I/O 操做後,要一直等到整個 I/O 完成後,才能得到I/O 響應。
所謂異步 I/O,是指應用程序執行 I/O 操做後,不用等待完成和完成後的響應,而是繼續執行就能夠。等到此次 I/O 完成後,響應會用事件通知的方式,告訴應用程序。

舉個例子,在操做文件時,若是你設置了 O_SYNC 或者 O_DSYNC 標誌,就表明同步I/O。若是設置了 O_DSYNC,就要等文件數據寫入磁盤後,才能返回;而 O_SYNC,則是在 O_DSYNC 基礎上,要求文件元數據也要寫入磁盤後,才能返回。

再好比,在訪問管道或者網絡套接字時,設置了 O_ASYNC 選項後,相應的 I/O 就是異步I/O。這樣,內核會再經過 SIGIO 或者 SIGPOLL,來通知進程文件是否可讀寫。

你可能發現了,這裏的好多概念也常常出如今網絡編程中。好比非阻塞 I/O,一般會跟select/poll 配合,用在網絡套接字的 I/O 中。

你也應該能夠理解,「Linux 一切皆文件」的深入含義。不管是普通文件和塊設備、仍是網絡套接字和管道等,它們都經過統一的 VFS 接口來訪問。

性能觀測

學了這麼多文件系統的原理,你估計也是火燒眉毛想上手,觀察一下文件系統的性能狀況了。

接下來,打開一個終端,SSH 登陸到服務器上,而後跟我一塊兒來探索,如何觀測文件系統的性能。

容量

對文件系統來講,最多見的一個問題就是空間不足。固然,你可能自己就知道,用 df 命令,就能查看文件系統的磁盤空間使用狀況。好比:

$ df /dev/sda1
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sda1       30308240 3167020  27124836  11% /

你能夠看到,個人根文件系統只使用了 11% 的空間。這裏還要注意,總空間用 1K-blocks的數量來表示,你能夠給 df 加上 -h 選項,以得到更好的可讀性:

$ df -h /dev/sda1
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        29G  3.1G   26G  11% /

不過有時候,明明你碰到了空間不足的問題,但是用 df 查看磁盤空間後,卻發現剩餘空間還有不少。這是怎麼回事呢?

不知道你還記不記得,剛纔我強調的一個細節。除了文件數據,索引節點也佔用磁盤空間。你能夠給 df 命令加上 -i 參數,查看索引節點的使用狀況,以下所示:

$ df -i /dev/sda1
Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/sda1      3870720 157460 3713260    5% /

索引節點的容量,(也就是 Inode 個數)是在格式化磁盤時設定好的,通常由格式化工具自動生成。當你發現索引節點空間不足,但磁盤空間充足時,極可能就是過多小文件致使的。

因此,通常來講,刪除這些小文件,或者把它們移動到索引節點充足的其餘磁盤中,就能夠解決這個問題。

緩存

在前面 Cache 案例中,我已經介紹過,能夠用 free 或 vmstat,來觀察頁緩存的大小。複習一下,free 輸出的 Cache,是頁緩存和可回收 Slab 緩存的和,你能夠從/proc/meminfo ,直接獲得它們的大小:

$ cat /proc/meminfo | grep -E "SReclaimable|Cached"
Cached:           748316 kB
SwapCached:            0 kB
SReclaimable:     179508 kB

話說回來,文件系統中的目錄項和索引節點緩存,又該如何觀察呢?

實際上,內核使用 Slab 機制,管理目錄項和索引節點的緩存。/proc/meminfo 只給出了Slab 的總體大小,具體到每一種 Slab 緩存,還要查看 /proc/slabinfo 這個文件。

好比,運行下面的命令,你就能夠獲得,全部目錄項和各類文件系統索引節點的緩存狀況:

$ cat /proc/slabinfo | grep -E '^#|dentry|inode'
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunabxfs_inode              0      0    960   17    4 : tunables    0    0    0 : slabdata
...
ext4_inode_cache   32104  34590   1088   15    4 : tunables    0    0    0 : slabdatasock_inode_cache    1190   1242    704   23    4 : tunables    0    0    0 : slabdatashmem_inode_cache   1622   2139    712   23    4 : tunables    0    0    0 : slabdataproc_inode_cache    3560   4080    680   12    2 : tunables    0    0    0 : slabdatainode_cache        25172  25818    608   13    2 : tunables    0    0    0 : slabdatadentry             76050 121296    192   21    1 : tunables    0    0    0 : slabdata

這個界面中,dentry 行表示目錄項緩存,inode_cache 行,表示 VFS 索引節點緩存,其他的則是各類文件系統的索引節點緩存。

/proc/slabinfo 的列比較多,具體含義你能夠查詢man slabinfo。在實際性能分析中,咱們更常使用 slabtop,來找到佔用內存最多的緩存類型。

好比,下面就是我運行 slabtop 獲得的結果:

#按下c按照緩存大小排序,按下a按照活躍對象數排序
$ slabtop
Active / Total Objects (% used)    : 277970 / 358914 (77.4%)
Active / Total Slabs (% used)      : 12414 / 12414 (100.0%)
Active / Total Caches (% used)     : 83 / 135 (61.5%)
Active / Total Size (% used)       : 57816.88K / 73307.70K (78.9%)
                                                            Minimum / Average / Maximum Object : 0.01K / 0.20K / 22.88K
OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
69804  23094   0%    0.19K   3324       21     13296K dentry
16380  15854   0%    0.59K   1260       13     10080K inode_cache58260  55397   0%    0.13K   1942       30      7768K kernfs_node_cache
485    413   0%    5.69K     97        5      3104K task_struct1472   1397   0%    2.00K     92       16      2944K kmalloc-2048

從這個結果你能夠看到,在個人系統中,目錄項和索引節點佔用了最多的 Slab 緩存。不過它們佔用的內存其實並不大,加起來也只有 23MB 左右。

總結

本文,我帶你梳理了 Linux 文件系統的工做原理。

文件系統,是對存儲設備上的文件,進行組織管理的一種機制。爲了支持各種不一樣的文件系統,Linux 在各類文件系統實現上,抽象了一層虛擬文件系統(VFS)。

VFS 定義了一組全部文件系統都支持的數據結構和標準接口。這樣,用戶進程和內核中的其餘子系統,就只須要跟 VFS 提供的統一接口進行交互。

爲了下降慢速磁盤對性能的影響,文件系統又經過頁緩存、目錄項緩存以及索引節點緩存,緩和磁盤延遲對應用程序的影響。

在性能觀測方面,本文主要講了容量和緩存的指標。下一節,咱們將會學習 Linux 磁盤 I/O的工做原理,並掌握磁盤 I/O 的性能觀測方法。

相關文章
相關標籤/搜索