天天看點

Linux檔案系統、磁盤I/O是怎麼工作的

作者:嵌入式Linux
點選左上方藍色“一口Linux”,選擇“設為星标1           

同CPU、記憶體一樣,檔案系統和磁盤I/O,也是Linux作業系統最核心的功能。

  • 磁盤為系統提供了最基本的持久化存儲。
  • 檔案系統則在磁盤基礎上,提供了一個用來管理檔案的樹狀結構。

檔案系統

1. 索引節點和目錄項

Linux中的一切都由統一的檔案系統來管理,包括普通的檔案和目錄,以及塊裝置、套接字、管道等。Linux檔案系統為每個檔案都配置設定了兩個資料結構,索引節點(index node)和目錄項(directory entry),主要用來記錄檔案的元資訊和目錄結構。

  • 索引節點,簡稱為 inode,用來記錄檔案的中繼資料,比如inode編号、檔案大小、通路權限、修改日期、資料的位置等。索引節點和檔案一一對應,它跟檔案内容一樣會被持久化到磁盤,是以,索引節點同樣占磁盤。
  • 目錄項,簡稱為dentry,用來記錄的檔案的名字、索引節點指針以及與其他目錄項的關聯關系。多個關聯的目錄項,就構成了檔案系統的目錄結構,它是由核心維護的一個記憶體資料結構,通常也被稱為目錄項緩存。

換句話說,索引節點是每個檔案的唯一标志,目錄項維護的是檔案系統的樹狀結構。目錄項和索引節點的關系是多對一,或者可了解為一個檔案多個别名。舉個例子,通過硬連結為檔案建立的别名,就會對應不同目錄項,這些目錄項本質上是連接配接同一個檔案,是以索引節點相同。

更具體地說,檔案資料是怎麼存儲的,是直接儲存到磁盤的?實際上磁盤讀寫的最小機關是扇區,扇區隻有512B大小,如果每次讀寫這麼小的機關,效率一定很低。是以,檔案系統又把連續的扇區組成邏輯塊,再以邏輯塊為最小單元去管理資料。常見的邏輯塊大小是4KB,即連續的8個扇區。下面展示一張示意圖:

Linux檔案系統、磁盤I/O是怎麼工作的

這裡需要注意兩點:

第一,目錄項本身在記憶體中,索引節點在磁盤中。前面的 Buffer 和 Cache 原理中提到,為了協調慢速磁盤和快速CPU之間的性能差異,檔案内容會緩存到頁緩存 Cache中。索引節點自然也會緩存到記憶體中,增加速檔案通路。

第二,磁盤在執行檔案系統格式化時,會被分成三個存儲區域,超級塊、索引節點區 和 資料塊區。其中,超級塊存儲整個檔案系統狀态;索引節點區存儲索引節點;資料塊區,存儲檔案資料。

2. 虛拟檔案系統

目錄項、索引節點、超級塊、邏輯塊構成Linux檔案系統四大基本要素。不過,為了支援各種不同的檔案系統,Linux核心在使用者程序和檔案系統中間,引入了一個抽象層,即虛拟檔案系統VFS。VFS定義了一套所有檔案系統都支援的資料結構和标準接口。這樣,使用者層和核心其他子系統都隻需要跟 VFS 提供的統一接口互動就可以了,不需要關心底層各種檔案系統的實作細節。下圖很好展示了Linux檔案系統的架構圖,能更好的幫助了解系統調用、VFS、緩存、檔案系統以及塊存儲之間的關系:

Linux檔案系統、磁盤I/O是怎麼工作的

從圖中可以看到,在VFS下面Linux可以支援各種檔案系統,按照存儲位置的不同,可以分為三類:

  • 基于磁盤的檔案系統,也就是把資料直接存儲到計算機本地挂載的磁盤中。如 EXT4、XFS、OverlayFS等。
  • 基于記憶體的檔案系統,也就是虛拟檔案系統,不需要磁盤配置設定任何存儲空間,隻占用記憶體。如 /proc 檔案系統、/sys 檔案系統(主要向使用者空間導出階層化的核心對象)。
  • 網絡檔案系統,用來通路其他計算機資料的檔案系統,如 NFS、SMB、iSCSI等。

這些檔案系統,要先挂載到 VFS 目錄樹中的子目錄(挂載點),然後才能通路其中檔案。比如安裝系統時,要先挂在一個根目錄( / ),在根目錄下,再把其他檔案系統挂在進來。

3. 檔案系統I/O

把檔案挂到挂載點後,就能通過它去通路它管理的檔案了。VFS提供的通路檔案的标準接口,以系統調用的方式提供給應用程式使用。比如,cat指令,相繼調用 open()、read()、write()。檔案讀寫方式的各種差異,也導緻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;如果沒獲得響應,卻不阻塞目前線程,繼續執行其他任務,随後通過輪詢或者時間通知的形式,獲得之前調用的結果。比如,通路管道或者網絡套接字時,設定 O_NONBLOCK 标志,表示非阻塞方式通路,若不做任何設定,預設就是阻塞方式通路。

第四種,根據是否等待響應結果,可以把檔案I/O 分為同步I/O 和 異步I/O。在應用程式執行I/O 操作後,如果一直等到 整個 I/O完成後才獲得響應,就是同步I/O;如果不等待 I/O 完成以及完成後的響應,繼續往下執行,等到 I/O 完成後,響應會用事件通知的方式,告訴應用程式。比如,在操作檔案時,如果設定了 O_SYNC 或 O_DSYNC标志,就代表同步I/O,後者是等待檔案資料寫入磁盤後才傳回,而前者是在後者基礎上,要求檔案中繼資料也要寫入磁盤後才能傳回。再比如,在通路管道或者網絡套接字時,設定選項 O_ASYNC後,就是異步 I/O核心會通過 SIGIO 或者 SIGPOLL,來通知程序,檔案是否可讀寫。

總之,無論是普通檔案和塊裝置、還是網絡套接字和管道等,都通過統一的VFS接口來被通路。

4. 檔案系統性能觀測

$ df /dev/sda1 
Filesystem     1K-blocks    Used Available Use% Mounted on 
/dev/sda1       30308240 3167020  27124836  11% /
 
$ df -h /dev/sda1 
Filesystem      Size  Used Avail Use% Mounted on 
/dev/sda1        29G  3.1G   26G  11% / 
 
$ df -i /dev/sda1 
Filesystem      Inodes  IUsed   IFree IUse% Mounted on 
/dev/sda1      3870720 157460 3713260    5% /           

加上-i 參數檢視索引節點的使用情況,索引節點的容量,(也就是 Inode個數)是在格式化磁盤時設定好的,由格式化工具自動生成。當你發現索引節點空間不足時,但磁盤空間充足時,很可能是過多的小檔案導緻的,一般的删除它們或者移到其他的索引節點充足的磁盤上,就能解決問題。

接下來,檔案系統的目錄項和索引節點的緩存,如何檢視呢?

實際上,核心使用 Slab 機制,管理目錄項和索引節點的緩存。/proc/meminfo 隻給出了Slab整體大小,具體到每一種Slab緩存,就要檢視 /proc/slabinfo。運作下面指令可以得到,所有目錄項和各種檔案系統的索引節點的緩存情況:

$ cat /proc/slabinfo | grep -E '^#|dentry|inode' 
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail> 
xfs_inode              0      0    960   17    4 : tunables    0    0    0 : slabdata      0      0      0 
... 
ext4_inode_cache   32104  34590   1088   15    4 : tunables    0    0    0 : slabdata   2306   2306      0hugetlbfs_inode_cache     13     13    624   13    2 : tunables    0    0    0 : slabdata      1      1      0 
sock_inode_cache    1190   1242    704   23    4 : tunables    0    0    0 : slabdata     54     54      0 
shmem_inode_cache   1622   2139    712   23    4 : tunables    0    0    0 : slabdata     93     93      0 
proc_inode_cache    3560   4080    680   12    2 : tunables    0    0    0 : slabdata    340    340      0 
inode_cache        25172  25818    608   13    2 : tunables    0    0    0 : slabdata   1986   1986      0 
dentry             76050 121296    192   21    1 : tunables    0    0    0 : slabdata   5776   5776      0           

dentry 行表示目錄項緩存,inode_cache 行,表示VFS 索引節點緩存,其餘的則是各種檔案系統的緩存。這裡列比較多,可查詢man slabinfo。實際性能分析時,更多使用 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_cache 
58260  55397   0%    0.13K   1942       30      7768K kernfs_node_cache 
   485    413   0%    5.69K     97        5      3104K task_struct 
  1472   1397   0%    2.00K     92       16      2944K kmalloc-2048           

從這個結果可以看到,目錄項和索引節點占用最多的 Slab緩存,但其實并不大,約23MB。

思考:find / -name file-name 指令導緻會不會導緻緩存升高,如果會,導緻哪類緩存升高呢?

find / -name 指令是全盤掃描(包括記憶體檔案系統、磁盤檔案系統等),是以這裡會導緻 xfs_inode 、proc_inode_cache、dentry、 inode_cache這幾類緩存的升高,而且在下次執行 find 指令時,就會快很多,因為它大部分會直接在緩存中查找結果。這裡你可以在執行find指令前後,比較slabtop、free、vmstat輸出結果,又會有更深的了解。

磁盤 I/O

1. 磁盤

首先,根據存儲媒體的不同,可以分為兩類,機械磁盤 和 固态磁盤。

機械磁盤:也稱為硬碟驅動器(Hard Disk Driver,縮寫HDD),機械磁盤由盤片和讀寫磁頭組成,資料存儲在盤片的環狀磁道中,最小讀寫機關 扇區,一般大小為512B。在讀寫資料時,需要移動磁頭,定位到資料所在的盤片磁道中,然後才通路資料。如果 I/O 請求剛好連續,那就不需要磁道尋址,可獲得最佳性能,這就是順序I/O 的工作原理。随機 I/O,需要不停地移動磁頭,來定位資料位置,讀寫速度比較慢。

固态磁盤:Solid State Driver,縮寫SSD,由固态電子元器件組成,最小讀寫機關 頁,一般大小4KB、8KB等。固态磁盤不需要磁道尋址,不管是連續I/O,還是随機I/O的性能,都比機械磁盤好得多。

另外,相同磁盤的順序I/O 都要比 随機I/O 快得多,原因如下:

  • 對于機械磁盤來說,随機 I/O需要更多的磁頭尋道和盤片旋轉,性能比順序I/O 慢。
  • 對于固态盤來說,雖然随機I/O 性能比機械盤好很多,但是它也會有“先擦除、再寫入”的限制。随機讀寫也有大量的垃圾回收,是以還是會比順序I/O 慢很多。
  • 另外,順序I/O 可以通過預讀的方式,來減少 I/O請求的次數,這也是其性能優異的原因之一。

在上一節提到過,如果每次都讀寫 512B 資料,效率會很低。檔案系統會把連續的扇區或頁組成邏輯塊,作為最小單元管理資料,常見的邏輯塊是 4KB,即連續的8個扇區,或者一個頁。

其次,還可以按照接口來分類,可以把硬碟分為 IDE、SCSI、SAS、SATA、FC等。不同的接口,配置設定不同的裝置名稱。比如 IDE的會配置設定一個字首為 hd 的裝置名,SCSI 和 SATA會配置設定一個 sd 字首的裝置名。如果是多塊同類型的磁盤,會按照a、b、c等字母順序編号。

第三,還可以根據使用方式,将磁盤劃分為不同架構。最簡單的就是,作為獨立磁盤來使用。然後再根據需要,将磁盤劃分成多個邏輯分區,再給分區編号。比如前面多次用到的 /dev/sda,還可以分成兩個分區 /dev/sda1 和 /dev/sda2。另一個比較常用的架構是,将多塊磁盤組成一個邏輯磁盤,構成備援獨立 的磁盤陣列,RAID,提高資料通路性能,增強資料存儲的可靠性。

根據容量、性能、可靠性的不同,RAID可以分為多個級别,如RAID0、RAID1、RAID5、RAID10等。RAID0有最優的讀寫性能,但不提供資料備援的功能,其他級别的 RAID,在資料備援的基礎上,對讀寫性能有一定的優化。

最後一種架構,把磁盤組合成網絡存儲叢集,再通過NFS、SMB、iSCSI等網絡存儲協定,暴露給伺服器使用。

其實,在Linux下,磁盤是作為塊裝置來管理的,也就是以塊為機關來讀寫資料,且支援随機讀寫。每個塊裝置都被賦予主、次兩個裝置号,主裝置号用在驅動程式中差別裝置類型,次裝置号用來給多個同類裝置編号。

2. 通用塊層

為了減少不同塊裝置的差異帶來的影響,Linux通過一個統一的通用塊層,來管理各種不同的塊裝置。通用塊層其實是處在檔案系統和磁盤驅動中間的一個塊裝置抽象層。有兩個功能:

第一個跟虛拟檔案系統的功能類似。向上,為檔案系統和應用程式,提供通路塊裝置的标準接口;向下,把各種異構的磁盤裝置抽象成統一的塊裝置,并提供統一架構來管理這些裝置的驅動程式。

第二個功能,通用塊層還給檔案系統和應用程式發來的I/O請求排隊,并通過請求排隊、合并等,提高磁盤讀寫的效率。

對 I/O請求排序也是 I/O排程。事實上,Linux核心支援四種 I/O排程算法,NONE、NOOP、CFQ、DeadLine。

NONE:确切的說并不能算排程,因為它完全不使用任何排程器,對檔案系統和應用程式的 I/O不作任何處理,常用在虛拟機中(此時磁盤 I/O排程完全由實體機支援)。

NOOP:最簡單的一種排程算法,是一個先進先出的隊列,隻做一些最基本的請求合并,常用于SSD盤。

CFQ:完全公平排程器,是現在很多發行版的預設 I/O排程器。它為每個程序維護了一個 I/O排程隊列,并按時間片來均勻分布每個程序的 I/O請求。類似于程序的CPU排程,CFQ排程還支援程序 I/O的優先級排程,是以适用運作着大量程序的系統,像桌面環境、多媒體應用等。

DeadLine:分别為讀、寫請求建立不同的 I/O 隊列,可以提高機械磁盤的吞吐量,并確定達到最終期限的請求被優先處理。這種排程算法 多用在 I/O 壓力比較大的場合,如資料庫等。

3. I/O棧

結合上面講的檔案系統、磁盤和通用塊層的工作原理,我們可以整體來看 Linux存儲系統的 I/O原理了。事實上,我們可以把 Linux存儲系統的 I/O棧,由上至下分為三層:檔案系統層、通用塊層、裝置層。看圖:

Linux檔案系統、磁盤I/O是怎麼工作的

根據這張全景圖,我們可以更清楚了解,存儲系統的 I/O的工作原理:

  • 檔案系統層,包括虛拟檔案系統和其他各種檔案系統的具體實作。首先為上層的應用程式提供标準的檔案通路接口,對下會通過通用塊層,來存儲和管理磁盤資料。
  • 通用塊層,是Linux磁盤 I/O的核心,包括裝置 I/O隊列和 I/O排程器。會對檔案系統的 I/O請求進行排隊,再通過重新排序和請求合并,再發給下一級裝置層。
  • 裝置層,包括儲存設備和相應的驅動程式,負責最終實體裝置的 I/O操作。

存儲系統的 I/O,通常是整個Linux系統中最慢的一環。是以,Linux通過多種緩存機制來優化 I/O 效率。比如,為了優化檔案通路性能,會使用頁緩存、索引節點緩存、目錄項緩存等多種緩存機制,減少對下層塊裝置的直接調用。同樣,為了優化塊裝置的通路性能,會使用緩沖區,來緩存塊裝置的資料。

嵌入式物聯網需要學的東西真的非常多,千萬不要學錯了路線和内容,導緻工資要不上去!

無償分享大家一個資料包,差不多150多G。裡面學習内容、面經、項目都比較新也比較全!某魚上買估計至少要好幾十。

點選這裡找小助理0元領取:加微信領取資料

Linux檔案系統、磁盤I/O是怎麼工作的
Linux檔案系統、磁盤I/O是怎麼工作的

4. 磁盤性能名額以及觀測

這裡說一下常見的五個名額,使用率、飽和度、IOPS、吞吐量以及響應時間等,這五個名額是衡量磁盤性能的基本名額。

  • 使用率,是指磁盤處理 I/O的時間百分比。過高的使用率(如超過80%),通常意味着磁盤 I/O的性能瓶頸。
  • 飽和度,磁盤處理 I/O的繁忙程度,過高的飽和度,意味着磁盤存在嚴重的性能瓶頸。當達到100%時,磁盤就無法接受新的 I/O請求。
  • IOPS,每秒的 I/O請求數。
  • 吞吐量,每秒的 I/O請求大小。
  • 響應時間,從送出請求到收到響應的時間間隔。

注意,使用率隻考慮有沒有 I/O,而不考慮 I/O大小,即使達到100%,也有可能接受新的 I/O請求。在資料庫、大量小檔案等這類随機讀寫比較多的場景中,IOPS更能反應系統整體性能。在多媒體等順序讀寫較多的場景中,吞吐量更能反應系統整體性能。

一般來說,我們在為應用程式的伺服器選型時,要先對磁盤 I/O的性能進行基準測試,推薦的性能測試工具 fio,來測試磁盤的 IOPS,吞吐量以及響應時間等核心名額。用性能工具得到的名額,作為後續分析應用程式的性能依據。一旦發生性能問題,就可以把它們作為磁盤性能的極限值,進而評估磁盤 I/O的使用情況。

接下來看看怎麼觀測磁盤 I/O?首推的工具 iostat,它提供每個磁盤的使用率、IOPS、吞吐量等各種常見的性能名額,當然這些名額來自 /proc/diskstats。iostats 的輸出界面如下:

# -d -x表示顯示所有磁盤I/O的名額
$ iostat -d -x 1 
Device            r/s     w/s     rkB/s     wkB/s   rrqm/s   wrqm/s  %rrqm  %wrqm r_await w_await aqu-sz rareq-sz wareq-sz  svctm  %util 
loop0            0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00 
loop1            0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00 
sda              0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00 
sdb              0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00           

下圖說明了這些列的具體含義:

Linux檔案系統、磁盤I/O是怎麼工作的

這些名額,你要注意,%util 磁盤使用率,r/s + w/s IOPS,rkB/s + wkB/s 吞吐量, r_await + w_await 響應時間。另外從 iostat 并不能直接得到磁盤的飽和度,但是可以把觀測到的,平均請求隊列長度 或者 讀寫請求完成的等待時間,跟基準測試的結果進行對比,綜合來評估。

我們再來看看,每個程序的 I/O情況。iostat隻能看到磁盤整體的 I/O性能資料,并不能知道具體哪些程序 在進行磁盤讀寫,推薦兩個工具:pidstat 和 iotop。具體使用這裡略過。

end

文章連結:https://mp.weixin.qq.com/s/pOifG5m1VkSUmKOFzdi-5Q

轉載自:洋芋居士 一口Linux

文章來源:Linux檔案系統、磁盤I/O是怎麼工作的

版權申明:本文來源于網絡,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯系我進行删除。