本文已收錄GitHub,更有互聯網大廠面試真題,面試攻略,高效學習資料等node
磁盤爲系統提供了最基本的持久化存儲。git
文件系統則在磁盤的基礎上,提供了一個用來管理文件的樹狀結構。github
那麼,磁盤和文件系統是怎麼工做的呢?又有哪些指標能夠衡量它們的性能呢?面試
文件系統,自己是對存儲設備上的文件,進行組織管理的機制。組織方式不一樣,就會造成不一樣的文件系統。數據庫
你要記住最重要的一點,在 Linux 中一切皆文件。不只普通的文件和目錄,就連塊設備、套接字、管道等,也都要經過統一的文件系統來管理。編程
爲了方便管理,Linux 文件系統爲每一個文件都分配兩個數據結構,索引節點(indexnode)和目錄項(directory entry)。它們主要用來記錄文件的元信息和目錄結構。緩存
索引節點,簡稱爲 inode,用來記錄文件的元數據,好比 inode 編號、文件大小、訪問權限、修改日期、數據的位置等。索引節點和文件一一對應,它跟文件內容同樣,都會被持久化存儲到磁盤中。因此記住,索引節點一樣佔用磁盤空間。服務器
目錄項,簡稱爲 dentry,用來記錄文件的名字、索引節點指針以及與其餘目錄項的關聯關係。多個關聯的目錄項,就構成了文件系統的目錄結構。不過,不一樣於索引節點,目錄項是由內核維護的一個內存數據結構,因此一般也被叫作目錄項緩存。網絡
換句話說,索引節點是每一個文件的惟一標誌,而目錄項維護的正是文件系統的樹狀結構。目錄項和索引節點的關係是多對一,你能夠簡單理解爲,一個文件能夠有多個別名。數據結構
舉個例子,經過硬連接爲文件建立的別名,就會對應不一樣的目錄項,不過這些目錄項本質上仍是連接同一個文件,因此,它們的索引節點相同。
索引節點和目錄項紀錄了文件的元數據,以及文件間的目錄關係,那麼具體來講,文件數據究竟是怎麼存儲的呢?是否是直接寫到磁盤中就行了呢?
實際上,磁盤讀寫的最小單位是扇區,然而扇區只有 512B 大小,若是每次都讀寫這麼小的單位,效率必定很低。因此,文件系統又把連續的扇區組成了邏輯塊,而後每次都以邏輯塊爲最小單元,來管理數據。常見的邏輯塊大小爲 4KB,也就是由連續的 8 個扇區組成。
爲了幫助你理解目錄項、索引節點以及文件數據的關係,我畫了一張示意圖。你能夠對照着這張圖,來回憶剛剛講過的內容,把知識和細節串聯起來。
不過,這裏有兩點須要你注意。
第一,目錄項自己就是一個內存緩存,而索引節點則是存儲在磁盤中的數據。在前面的Buffer 和 Cache 原理中,我曾經提到過,爲了協調慢速磁盤與快速 CPU 的性能差別,文件內容會緩存到頁緩存 Cache 中。
那麼,你應該想到,這些索引節點天然也會緩存到內存中,加速文件的訪問。
第二,磁盤在執行文件系統格式化時,會被分紅三個存儲區域,超級塊、索引節點區和數據塊區。其中,
目錄項、索引節點、邏輯塊以及超級塊,構成了 Linux 文件系統的四大基本要素。不過,爲了支持各類不一樣的文件系統,Linux 內核在用戶進程和文件系統的中間,又引入了一個抽象層,也就是虛擬文件系統 VFS(Virtual File System)。
VFS 定義了一組全部文件系統都支持的數據結構和標準接口。這樣,用戶進程和內核中的其餘子系統,只須要跟 VFS 提供的統一接口進行交互就能夠了,而不須要再關心底層各類文件系統的實現細節。
這裏,我畫了一張 Linux 文件系統的架構圖,幫你更好地理解系統調用、VFS、緩存、文件系統以及塊存儲之間的關係。
經過這張圖,你能夠看到,在 VFS 的下方,Linux 支持各類各樣的文件系統,如 Ext四、XFS、NFS 等等。按照存儲位置的不一樣,這些文件系統能夠分爲三類。
這些文件系統,要先掛載到 VFS 目錄樹中的某個子目錄(稱爲掛載點),而後才能訪問其中的文件。拿第一類,也就是基於磁盤的文件系統爲例,在安裝系統時,要先掛載一個根目錄(/),在根目錄下再把其餘文件系統(好比其餘的磁盤分區、/proc 文件系統、/sys 文件系統、NFS 等)掛載進來。
把文件系統掛載到掛載點後,你就能經過掛載點,再去訪問它管理的文件了。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 的性能觀測方法。