同前面講述的CPU、內存一樣,文件系統(tǒng)和磁盤I/O,也是Linux操作系統(tǒng)最核心的功能。
- 磁盤為系統(tǒng)提供了最基本的持久化存儲。
- 文件系統(tǒng)則在磁盤基礎上,提供了一個用來管理文件的樹狀結構。
文件系統(tǒng)
1. 索引節(jié)點和目錄項
Linux中的一切都由統(tǒng)一的文件系統(tǒng)來管理,包括普通的文件和目錄,以及塊設備、套接字、管道等。Linux文件系統(tǒng)為每個文件都分配了兩個數(shù)據(jù)結構,索引節(jié)點(index node)和目錄項(directory entry),主要用來記錄文件的元信息和目錄結構。
- 索引節(jié)點,簡稱為 inode,用來記錄文件的元數(shù)據(jù),比如inode編號、文件大小、訪問權限、修改日期、數(shù)據(jù)的位置等。索引節(jié)點和文件一一對應,它跟文件內容一樣會被持久化到磁盤,所以,索引節(jié)點同樣占磁盤。
- 目錄項,簡稱為dentry,用來記錄的文件的名字、索引節(jié)點指針以及與其他目錄項的關聯(lián)關系。多個關聯(lián)的目錄項,就構成了文件系統(tǒng)的目錄結構,它是由內核維護的一個內存數(shù)據(jù)結構,通常也被稱為目錄項緩存。
換句話說,索引節(jié)點是每個文件的唯一標志,目錄項維護的是文件系統(tǒng)的樹狀結構。目錄項和索引節(jié)點的關系是多對一,或者可理解為一個文件多個別名。舉個例子,通過硬鏈接為文件創(chuàng)建的別名,就會對應不同目錄項,這些目錄項本質上是連接同一個文件,所以索引節(jié)點相同。
更具體地說,文件數(shù)據(jù)是怎么存儲的,是直接保存到磁盤的?實際上磁盤讀寫的最小單位是扇區(qū),扇區(qū)只有512B大小,如果每次讀寫這么小的單位,效率一定很低。所以,文件系統(tǒng)又把連續(xù)的扇區(qū)組成邏輯塊,再以邏輯塊為最小單元去管理數(shù)據(jù)。常見的邏輯塊大小是4KB,即連續(xù)的8個扇區(qū)。下面展示一張示意圖:
這里需要注意兩點:
第一,目錄項本身在內存中,索引節(jié)點在磁盤中。前面的 Buffer 和 Cache 原理中提到,為了協(xié)調慢速磁盤和快速CPU之間的性能差異,文件內容會緩存到頁緩存 Cache中。索引節(jié)點自然也會緩存到內存中,增加速文件訪問。
第二,磁盤在執(zhí)行文件系統(tǒng)格式化時,會被分成三個存儲區(qū)域,超級塊、索引節(jié)點區(qū) 和 數(shù)據(jù)塊區(qū)。其中,超級塊存儲整個文件系統(tǒng)狀態(tài);索引節(jié)點區(qū)存儲索引節(jié)點;數(shù)據(jù)塊區(qū),存儲文件數(shù)據(jù)。
2. 虛擬文件系統(tǒng)
目錄項、索引節(jié)點、超級塊、邏輯塊構成Linux文件系統(tǒng)四大基本要素。不過,為了支持各種不同的文件系統(tǒng),Linux內核在用戶進程和文件系統(tǒng)中間,引入了一個抽象層,即虛擬文件系統(tǒng)VFS。VFS定義了一套所有文件系統(tǒng)都支持的數(shù)據(jù)結構和標準接口。這樣,用戶層和內核其他子系統(tǒng)都只需要跟 VFS 提供的統(tǒng)一接口交互就可以了,不需要關心底層各種文件系統(tǒng)的實現(xiàn)細節(jié)。下圖很好展示了Linux文件系統(tǒng)的架構圖,能更好的幫助理解系統(tǒng)調用、VFS、緩存、文件系統(tǒng)以及塊存儲之間的關系:
從圖中可以看到,在VFS下面Linux可以支持各種文件系統(tǒng),按照存儲位置的不同,可以分為三類:
- 基于磁盤的文件系統(tǒng),也就是把數(shù)據(jù)直接存儲到計算機本地掛載的磁盤中。如 EXT4、XFS、OverlayFS等。
- 基于內存的文件系統(tǒng),也就是虛擬文件系統(tǒng),不需要磁盤分配任何存儲空間,只占用內存。如 /proc 文件系統(tǒng)、/sys 文件系統(tǒng)(主要向用戶空間導出層次化的內核對象)。
- 網絡文件系統(tǒng),用來訪問其他計算機數(shù)據(jù)的文件系統(tǒng),如 NFS、SMB、iSCSI等。
這些文件系統(tǒng),要先掛載到 VFS 目錄樹中的子目錄(掛載點),然后才能訪問其中文件。比如安裝系統(tǒng)時,要先掛在一個根目錄( / ),在根目錄下,再把其他文件系統(tǒng)掛在進來。
3. 文件系統(tǒng)I/O
把文件掛到掛載點后,就能通過它去訪問它管理的文件了。VFS提供的訪問文件的標準接口,以系統(tǒng)調用的方式提供給應用程序使用。比如,cat命令,相繼調用 open()、read()、write()。文件讀寫方式的各種差異,也導致I/O 的分類多種多樣。常見的有,緩沖與非緩沖I/O、直接與非直接I/O、阻塞與非阻塞I/O、同步與異步I/O等。下面詳細解釋下這四種 I/O分類:
第一種,根據(jù)是否利用標準庫緩存,可以把文件I/O 分為 緩沖I/O 和 非緩沖I/O。這里的“緩沖”,其實指的是標準庫內部實現(xiàn)的緩存。例如,很多程序遇到換行時才真正輸出,換行前的內容,就是被標準庫暫時緩存起來。因此,緩沖I/O 指的是利用標準庫緩存來加速文件的訪問,在標準庫內部再通過系統(tǒng)調用訪問文件;非緩沖I/O 指的是直接通過系統(tǒng)調用訪問文件,而不通過標準庫緩存。無論是緩沖還是非緩沖 I/O,最后都是通過系統(tǒng)調用訪問文件。而根據(jù)前面內容,系統(tǒng)調用后,還通過頁緩存,來減少磁盤I/O操作。
第二種,根據(jù)是否利用操作系統(tǒng)的頁緩存,可以把文件I/O 分為直接I/O 和 非直接I/O。想要實現(xiàn)直接I/O,需要在系統(tǒng)調用中指定標志 O_DIRECT,如果不指定,默認是非直接I/O。不過注意,這里的直接、非直接I/O,其實最終還是和文件系統(tǒng)交互。如果實在數(shù)據(jù)庫等場景中,還會看到,跳過文件系統(tǒng)讀寫磁盤的情況,即裸I/O。
第三種,根據(jù)應用程序是否阻塞自身運行,可以把文件I/O 分為阻塞I/O 和 非阻塞I/O。在應用程序執(zhí)行I/O 操作后,如果沒獲得響應,就阻塞當前線程,自然不能執(zhí)行其他任務,這是阻塞I/O;如果沒獲得響應,卻不阻塞當前線程,繼續(xù)執(zhí)行其他任務,隨后通過輪詢或者時間通知的形式,獲得之前調用的結果。比如,訪問管道或者網絡套接字時,設置 O_NONBLOCK 標志,表示非阻塞方式訪問,若不做任何設置,默認就是阻塞方式訪問。
第四種,根據(jù)是否等待響應結果,可以把文件I/O 分為同步I/O 和 異步I/O。在應用程序執(zhí)行I/O 操作后,如果一直等到 整個 I/O完成后才獲得響應,就是同步I/O;如果不等待 I/O 完成以及完成后的響應,繼續(xù)往下執(zhí)行,等到 I/O 完成后,響應會用事件通知的方式,告訴應用程序。比如,在操作文件時,如果設置了 O_SYNC 或 O_DSYNC標志,就代表同步I/O,后者是等待文件數(shù)據(jù)寫入磁盤后才返回,而前者是在后者基礎上,要求文件元數(shù)據(jù)也要寫入磁盤后才能返回。再比如,在訪問管道或者網絡套接字時,設置選項 O_ASYNC后,就是異步 I/O內核會通過 SIGIO 或者 SIGPOLL,來通知進程,文件是否可讀寫。
總之,無論是普通文件和塊設備、還是網絡套接字和管道等,都通過統(tǒng)一的VFS接口來被訪問。
4. 文件系統(tǒng)性能觀測
$ 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 參數(shù)查看索引節(jié)點的使用情況,索引節(jié)點的容量,(也就是 Inode個數(shù))是在格式化磁盤時設定好的,由格式化工具自動生成。當你發(fā)現(xiàn)索引節(jié)點空間不足時,但磁盤空間充足時,很可能是過多的小文件導致的,一般的刪除它們或者移到其他的索引節(jié)點充足的磁盤上,就能解決問題。
接下來,文件系統(tǒng)的目錄項和索引節(jié)點的緩存,如何查看呢?
實際上,內核使用 Slab 機制,管理目錄項和索引節(jié)點的緩存。/proc/meminfo 只給出了Slab整體大小,具體到每一種Slab緩存,就要查看 /proc/slabinfo。運行下面命令可以得到,所有目錄項和各種文件系統(tǒng)的索引節(jié)點的緩存情況:
$ cat /proc/slabinfo | grep -E '^#|dentry|inode'
# name < active_objs > < num_objs > < objsize > < objperslab > < pagesperslab > : tunables < limit >
dentry 行表示目錄項緩存,inode_cache 行,表示VFS 索引節(jié)點緩存,其余的則是各種文件系統(tǒng)的緩存。這里列比較多,可查詢man slabinfo。實際性能分析時,更多使用 slabtop,來找到占用內存最多的緩存類型:
# 按下c按照緩存大小排序,按下a按照活躍對象數(shù)排序
$ 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
從這個結果可以看到,目錄項和索引節(jié)點占用最多的 Slab緩存,但其實并不大,約23MB。
思考:find / -name file-name 命令導致會不會導致緩存升高,如果會,導致哪類緩存升高呢?
find / -name 命令是全盤掃描(包括內存文件系統(tǒng)、磁盤文件系統(tǒng)等),所以這里會導致 xfs_inode 、proc_inode_cache、dentry、 inode_cache這幾類緩存的升高,而且在下次執(zhí)行 find 命令時,就會快很多,因為它大部分會直接在緩存中查找結果。這里你可以在執(zhí)行find命令前后,比較slabtop、free、vmstat輸出結果,又會有更深的理解。
磁盤 I/O
1. 磁盤
首先,根據(jù)存儲介質的不同,可以分為兩類,機械磁盤 和 固態(tài)磁盤。
機械磁盤:也稱為硬盤驅動器(Hard Disk Driver,縮寫HDD),機械磁盤由盤片和讀寫磁頭組成,數(shù)據(jù)存儲在盤片的環(huán)狀磁道中,最小讀寫單位 扇區(qū),一般大小為512B。在讀寫數(shù)據(jù)時,需要移動磁頭,定位到數(shù)據(jù)所在的盤片磁道中,然后才訪問數(shù)據(jù)。如果 I/O 請求剛好連續(xù),那就不需要磁道尋址,可獲得最佳性能,這就是順序I/O 的工作原理。隨機 I/O,需要不停地移動磁頭,來定位數(shù)據(jù)位置,讀寫速度比較慢。
固態(tài)磁盤:Solid State Driver,縮寫SSD,由固態(tài)電子元器件組成,最小讀寫單位 頁,一般大小4KB、8KB等。固態(tài)磁盤不需要磁道尋址,不管是連續(xù)I/O,還是隨機I/O的性能,都比機械磁盤好得多。
另外,相同磁盤的順序I/O 都要比 隨機I/O 快得多,原因如下:
- 對于機械磁盤來說,隨機 I/O需要更多的磁頭尋道和盤片旋轉,性能比順序I/O 慢。
- 對于固態(tài)盤來說,雖然隨機I/O 性能比機械盤好很多,但是它也會有“先擦除、再寫入”的限制。隨機讀寫也有大量的垃圾回收,所以還是會比順序I/O 慢很多。
- 另外,順序I/O 可以通過預讀的方式,來減少 I/O請求的次數(shù),這也是其性能優(yōu)異的原因之一。
在上一節(jié)提到過,如果每次都讀寫 512B 數(shù)據(jù),效率會很低。文件系統(tǒng)會把連續(xù)的扇區(qū)或頁組成邏輯塊,作為最小單元管理數(shù)據(jù),常見的邏輯塊是 4KB,即連續(xù)的8個扇區(qū),或者一個頁。
其次,還可以按照接口來分類,可以把硬盤分為 IDE、SCSI、SAS、SATA、FC等。不同的接口,分配不同的設備名稱。比如 IDE的會分配一個前綴為 hd 的設備名,SCSI 和 SATA會分配一個 sd 前綴的設備名。如果是多塊同類型的磁盤,會按照a、b、c等字母順序編號。
第三,還可以根據(jù)使用方式,將磁盤劃分為不同架構。最簡單的就是,作為獨立磁盤來使用。然后再根據(jù)需要,將磁盤劃分成多個邏輯分區(qū),再給分區(qū)編號。比如前面多次用到的 /dev/sda,還可以分成兩個分區(qū) /dev/sda1 和 /dev/sda2。另一個比較常用的架構是,將多塊磁盤組成一個邏輯磁盤,構成冗余獨立 的磁盤陣列,RAID,提高數(shù)據(jù)訪問性能,增強數(shù)據(jù)存儲的可靠性。
根據(jù)容量、性能、可靠性的不同,RAID可以分為多個級別,如RAID0、RAID1、RAID5、RAID10等。RAID0有最優(yōu)的讀寫性能,但不提供數(shù)據(jù)冗余的功能,其他級別的 RAID,在數(shù)據(jù)冗余的基礎上,對讀寫性能有一定的優(yōu)化。
最后一種架構,把磁盤組合成網絡存儲集群,再通過NFS、SMB、iSCSI等網絡存儲協(xié)議,暴露給服務器使用。
其實,在Linux下,磁盤是作為塊設備來管理的,也就是以塊為單位來讀寫數(shù)據(jù),且支持隨機讀寫。每個塊設備都被賦予主、次兩個設備號,主設備號用在驅動程序中區(qū)別設備類型,次設備號用來給多個同類設備編號。
2. 通用塊層
為了減少不同塊設備的差異帶來的影響,Linux通過一個統(tǒng)一的通用塊層,來管理各種不同的塊設備。通用塊層其實是處在文件系統(tǒng)和磁盤驅動中間的一個塊設備抽象層。有兩個功能:
第一個跟虛擬文件系統(tǒng)的功能類似。向上,為文件系統(tǒng)和應用程序,提供訪問塊設備的標準接口;向下,把各種異構的磁盤設備抽象成統(tǒng)一的塊設備,并提供統(tǒng)一框架來管理這些設備的驅動程序。
第二個功能,通用塊層還給文件系統(tǒng)和應用程序發(fā)來的I/O請求排隊,并通過請求排隊、合并等,提高磁盤讀寫的效率。
對 I/O請求排序也是 I/O調度。事實上,Linux內核支持四種 I/O調度算法,NONE、NOOP、CFQ、DeadLine。
NONE:確切的說并不能算調度,因為它完全不使用任何調度器,對文件系統(tǒng)和應用程序的 I/O不作任何處理,常用在虛擬機中(此時磁盤 I/O調度完全由物理機支持)。
NOOP:最簡單的一種調度算法,是一個先進先出的隊列,只做一些最基本的請求合并,常用于SSD盤。
CFQ:完全公平調度器,是現(xiàn)在很多發(fā)行版的默認 I/O調度器。它為每個進程維護了一個 I/O調度隊列,并按時間片來均勻分布每個進程的 I/O請求。類似于進程的CPU調度,CFQ調度還支持進程 I/O的優(yōu)先級調度,所以適用運行著大量進程的系統(tǒng),像桌面環(huán)境、多媒體應用等。
DeadLine:分別為讀、寫請求創(chuàng)建不同的 I/O 隊列,可以提高機械磁盤的吞吐量,并確保達到最終期限的請求被優(yōu)先處理。這種調度算法 多用在 I/O 壓力比較大的場合,如數(shù)據(jù)庫等。
3. I/O棧
結合上面講的文件系統(tǒng)、磁盤和通用塊層的工作原理,我們可以整體來看 Linux存儲系統(tǒng)的 I/O原理了。事實上,我們可以把 Linux存儲系統(tǒng)的 I/O棧,由上至下分為三層:文件系統(tǒng)層、通用塊層、設備層。看圖:
根據(jù)這張全景圖,我們可以更清楚理解,存儲系統(tǒng)的 I/O的工作原理:
- 文件系統(tǒng)層,包括虛擬文件系統(tǒng)和其他各種文件系統(tǒng)的具體實現(xiàn)。首先為上層的應用程序提供標準的文件訪問接口,對下會通過通用塊層,來存儲和管理磁盤數(shù)據(jù)。
- 通用塊層,是Linux磁盤 I/O的核心,包括設備 I/O隊列和 I/O調度器。會對文件系統(tǒng)的 I/O請求進行排隊,再通過重新排序和請求合并,再發(fā)給下一級設備層。
- 設備層,包括存儲設備和相應的驅動程序,負責最終物理設備的 I/O操作。
存儲系統(tǒng)的 I/O,通常是整個Linux系統(tǒng)中最慢的一環(huán)。所以,Linux通過多種緩存機制來優(yōu)化 I/O 效率。比如,為了優(yōu)化文件訪問性能,會使用頁緩存、索引節(jié)點緩存、目錄項緩存等多種緩存機制,減少對下層塊設備的直接調用。同樣,為了優(yōu)化塊設備的訪問性能,會使用緩沖區(qū),來緩存塊設備的數(shù)據(jù)。
4. 磁盤性能指標以及觀測
這里說一下常見的五個指標,使用率、飽和度、IOPS、吞吐量以及響應時間等,這五個指標是衡量磁盤性能的基本指標。
- 使用率,是指磁盤處理 I/O的時間百分比。過高的使用率(如超過80%),通常意味著磁盤 I/O的性能瓶頸。
- 飽和度,磁盤處理 I/O的繁忙程度,過高的飽和度,意味著磁盤存在嚴重的性能瓶頸。當達到100%時,磁盤就無法接受新的 I/O請求。
- IOPS,每秒的 I/O請求數(shù)。
- 吞吐量,每秒的 I/O請求大小。
- 響應時間,從發(fā)出請求到收到響應的時間間隔。
注意,使用率只考慮有沒有 I/O,而不考慮 I/O大小,即使達到100%,也有可能接受新的 I/O請求。在數(shù)據(jù)庫、大量小文件等這類隨機讀寫比較多的場景中,IOPS更能反應系統(tǒng)整體性能。在多媒體等順序讀寫較多的場景中,吞吐量更能反應系統(tǒng)整體性能。
一般來說,我們在為應用程序的服務器選型時,要先對磁盤 I/O的性能進行基準測試,推薦的性能測試工具 fio,來測試磁盤的 IOPS,吞吐量以及響應時間等核心指標。用性能工具得到的指標,作為后續(xù)分析應用程序的性能依據(jù)。一旦發(fā)生性能問題,就可以把它們作為磁盤性能的極限值,進而評估磁盤 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性能數(shù)據(jù),并不能知道具體哪些進程 在進行磁盤讀寫,推薦兩個工具:pidstat 和 iotop。具體使用這里略過。
-
數(shù)據(jù)
+關注
關注
8文章
6715瀏覽量
88316 -
Linux
+關注
關注
87文章
11123瀏覽量
207919 -
磁盤
+關注
關注
1文章
355瀏覽量
25095 -
文件系統(tǒng)
+關注
關注
0文章
280瀏覽量
19831
發(fā)布評論請先 登錄
相關推薦
評論