同前面講述的CPU、內存一樣,文件系統和磁盤I/O,也是Linux操作系統最核心的功能。
- 磁盤為系統提供了最基本的持久化存儲。
- 文件系統則在磁盤基礎上,提供了一個用來管理文件的樹狀結構。
文件系統
1. 索引節點和目錄項
Linux中的一切都由統一的文件系統來管理,包括普通的文件和目錄,以及塊設備、套接字、管道等。Linux文件系統為每個文件都分配了兩個數據結構,索引節點(index node)和目錄項(directory entry),主要用來記錄文件的元信息和目錄結構。
- 索引節點,簡稱為 inode,用來記錄文件的元數據,比如inode編號、文件大小、訪問權限、修改日期、數據的位置等。索引節點和文件一一對應,它跟文件內容一樣會被持久化到磁盤,所以,索引節點同樣占磁盤。
- 目錄項,簡稱為dentry,用來記錄的文件的名字、索引節點指針以及與其他目錄項的關聯關系。多個關聯的目錄項,就構成了文件系統的目錄結構,它是由內核維護的一個內存數據結構,通常也被稱為目錄項緩存。
換句話說,索引節點是每個文件的唯一標志,目錄項維護的是文件系統的樹狀結構。目錄項和索引節點的關系是多對一,或者可理解為一個文件多個別名。舉個例子,通過硬鏈接為文件創建的別名,就會對應不同目錄項,這些目錄項本質上是連接同一個文件,所以索引節點相同。
更具體地說,文件數據是怎么存儲的,是直接保存到磁盤的?實際上磁盤讀寫的最小單位是扇區,扇區只有512B大小,如果每次讀寫這么小的單位,效率一定很低。所以,文件系統又把連續的扇區組成邏輯塊,再以邏輯塊為最小單元去管理數據。常見的邏輯塊大小是4KB,即連續的8個扇區。下面展示一張示意圖:
這里需要注意兩點:
第一,目錄項本身在內存中,索引節點在磁盤中。前面的 Buffer 和 Cache 原理中提到,為了協調慢速磁盤和快速CPU之間的性能差異,文件內容會緩存到頁緩存 Cache中。索引節點自然也會緩存到內存中,增加速文件訪問。
第二,磁盤在執行文件系統格式化時,會被分成三個存儲區域,超級塊、索引節點區 和 數據塊區。其中,超級塊存儲整個文件系統狀態;索引節點區存儲索引節點;數據塊區,存儲文件數據。
2. 虛擬文件系統
目錄項、索引節點、超級塊、邏輯塊構成Linux文件系統四大基本要素。不過,為了支持各種不同的文件系統,Linux內核在用戶進程和文件系統中間,引入了一個抽象層,即虛擬文件系統VFS。VFS定義了一套所有文件系統都支持的數據結構和標準接口。這樣,用戶層和內核其他子系統都只需要跟 VFS 提供的統一接口交互就可以了,不需要關心底層各種文件系統的實現細節。下圖很好展示了Linux文件系統的架構圖,能更好的幫助理解系統調用、VFS、緩存、文件系統以及塊存儲之間的關系:
從圖中可以看到,在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 >
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棧,由上至下分為三層:文件系統層、通用塊層、設備層。看圖:
根據這張全景圖,我們可以更清楚理解,存儲系統的 I/O的工作原理:
- 文件系統層,包括虛擬文件系統和其他各種文件系統的具體實現。首先為上層的應用程序提供標準的文件訪問接口,對下會通過通用塊層,來存儲和管理磁盤數據。
- 通用塊層,是Linux磁盤 I/O的核心,包括設備 I/O隊列和 I/O調度器。會對文件系統的 I/O請求進行排隊,再通過重新排序和請求合并,再發給下一級設備層。
- 設備層,包括存儲設備和相應的驅動程序,負責最終物理設備的 I/O操作。
存儲系統的 I/O,通常是整個Linux系統中最慢的一環。所以,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
下圖說明了這些列的具體含義:
這些指標,你要注意,%util 磁盤使用率,r/s + w/s IOPS,rkB/s + wkB/s 吞吐量, r_await + w_await 響應時間。另外從 iostat 并不能直接得到磁盤的飽和度,但是可以把觀測到的,平均請求隊列長度 或者 讀寫請求完成的等待時間,跟基準測試的結果進行對比,綜合來評估。
我們再來看看,每個進程的 I/O情況。iostat只能看到磁盤整體的 I/O性能數據,并不能知道具體哪些進程 在進行磁盤讀寫,推薦兩個工具:pidstat 和 iotop。具體使用這里略過。
-
數據
+關注
關注
8文章
7085瀏覽量
89202 -
Linux
+關注
關注
87文章
11322瀏覽量
209857 -
磁盤
+關注
關注
1文章
379瀏覽量
25224 -
文件系統
+關注
關注
0文章
287瀏覽量
19928
發布評論請先 登錄
相關推薦
評論