文件系統(tǒng)是對(duì)存儲(chǔ)設(shè)備上的文件,進(jìn)行組織管理的一種機(jī)制。而 Linux 在各種文件系統(tǒng)實(shí)現(xiàn)上,又抽象了一層虛擬文件系統(tǒng) VFS,它定義了一組,所有文件系統(tǒng)都支持的,數(shù)據(jù)結(jié)構(gòu)和標(biāo)準(zhǔn)接口。
這樣,對(duì)應(yīng)用程序來說,只需要跟 VFS 提供的統(tǒng)一接口交互,而不需要關(guān)注文件系統(tǒng)的具體實(shí)現(xiàn);對(duì)具體的文件系統(tǒng)來說,只需要按照 VFS 的標(biāo)準(zhǔn),就可以無縫支持各種應(yīng)用程序。
VFS 內(nèi)部又通過目錄項(xiàng)、索引節(jié)點(diǎn)、邏輯塊以及超級(jí)塊等數(shù)據(jù)結(jié)構(gòu),來管理文件。
- 目錄項(xiàng),記錄了文件的名字,以及文件與其他目錄項(xiàng)之間的目錄關(guān)系。
- 索引節(jié)點(diǎn),記錄了文件的元數(shù)據(jù)。
- 邏輯塊,是由連續(xù)磁盤扇區(qū)構(gòu)成的最小讀寫單元,用來存儲(chǔ)文件數(shù)據(jù)。
- 超級(jí)塊,用來記錄文件系統(tǒng)整體的狀態(tài),如索引節(jié)點(diǎn)和邏輯塊的使用情況等。
其中,目錄項(xiàng)是一個(gè)內(nèi)存緩存;而超級(jí)塊、索引節(jié)點(diǎn)和邏輯塊,都是存儲(chǔ)在磁盤中的持久化數(shù)據(jù)。
那么,進(jìn)一步想,磁盤又是怎么工作的呢?又有哪些指標(biāo)可以用來衡量它的性能呢?
接下來,我就帶你一起看看, Linux 磁盤 I/O 的工作原理。
磁盤
盤是可以持久化存儲(chǔ)的設(shè)備,根據(jù)存儲(chǔ)介質(zhì)的不同,常見磁盤可以分為兩類:機(jī)械磁盤和固態(tài)磁盤。
第一類,機(jī)械磁盤,也稱為硬盤驅(qū)動(dòng)器(Hard Disk Driver),通??s寫為 HDD。機(jī)械磁盤主要由盤片和讀寫磁頭組成,數(shù)據(jù)就存儲(chǔ)在盤片的環(huán)狀磁道中。在讀寫數(shù)據(jù)前,需要移動(dòng)讀寫磁頭,定位到數(shù)據(jù)所在的磁道,然后才能訪問數(shù)據(jù)。
顯然,如果 I/O 請(qǐng)求剛好連續(xù),那就不需要磁道尋址,自然可以獲得最佳性能。這其實(shí)就是我們熟悉的,連續(xù) I/O 的工作原理。與之相對(duì)應(yīng)的,當(dāng)然就是隨機(jī) I/O,它需要不停地移動(dòng)磁頭,來定位數(shù)據(jù)位置,所以讀寫速度就會(huì)比較慢。
第二類,固態(tài)磁盤(Solid State Disk),通??s寫為 SSD,由固態(tài)電子元器件組成。固態(tài)磁盤不需要磁道尋址,所以,不管是連續(xù) I/O,還是隨機(jī) I/O 的性能,都比機(jī)械磁盤要好得多。
其實(shí),無論機(jī)械磁盤,還是固態(tài)磁盤,相同磁盤的隨機(jī) I/O 都要比連續(xù) I/O 慢很多,原因也很明顯。
對(duì)機(jī)械磁盤來說,我們剛剛提到過的,由于隨機(jī) I/O 需要更多的磁頭尋道和盤片旋轉(zhuǎn),它的性能自然要比連續(xù) I/O 慢。
而對(duì)固態(tài)磁盤來說,雖然它的隨機(jī)性能比機(jī)械硬盤好很多,但同樣存在“先擦除再寫入”的限制。隨機(jī)讀寫會(huì)導(dǎo)致大量的垃圾回收,所以相對(duì)應(yīng)的,隨機(jī) I/O 的性能比起連續(xù) I/O 來,也還是差了很多。
此外,連續(xù) I/O 還可以通過預(yù)讀的方式,來減少 I/O 請(qǐng)求的次數(shù),這也是其性能優(yōu)異的一個(gè)原因。很多性能優(yōu)化的方案,也都會(huì)從這個(gè)角度出發(fā),來優(yōu)化 I/O 性能。
此外,機(jī)械磁盤和固態(tài)磁盤還分別有一個(gè)最小的讀寫單位。
- 機(jī)械磁盤的最小讀寫單位是扇區(qū),一般大小為 512 字節(jié)。
- 而固態(tài)磁盤的最小讀寫單位是頁,通常大小是 4KB、8KB 等。
在上一節(jié)中,我也提到過,如果每次都讀寫 512 字節(jié)這么小的單位的話,效率很低。所以,文件系統(tǒng)會(huì)把連續(xù)的扇區(qū)或頁,組成邏輯塊,然后以邏輯塊作為最小單元來管理數(shù)據(jù)。常見的邏輯塊的大小是 4KB,也就是說,連續(xù) 8 個(gè)扇區(qū),或者單獨(dú)的一個(gè)頁,都可以組成一個(gè)邏輯塊。
除了可以按照存儲(chǔ)介質(zhì)來分類,另一個(gè)常見的分類方法,是按照接口來分類,比如可以把硬盤分為 IDE(Integrated Drive Electronics)、SCSI(Small Computer System Interface) 、SAS(Serial Attached SCSI) 、SATA(Serial ATA) 、FC(Fibre Channel) 等。
不同的接口,往往分配不同的設(shè)備名稱。比如, IDE 設(shè)備會(huì)分配一個(gè) hd 前綴的設(shè)備名,SCSI 和 SATA 設(shè)備會(huì)分配一個(gè) sd 前綴的設(shè)備名。如果是多塊同類型的磁盤,就會(huì)按照 a、b、c 等的字母順序來編號(hào)。
除了磁盤本身的分類外,當(dāng)你把磁盤接入服務(wù)器后,按照不同的使用方式,又可以把它們劃分為多種不同的架構(gòu)。
最簡(jiǎn)單的,就是直接作為獨(dú)立磁盤設(shè)備來使用。這些磁盤,往往還會(huì)根據(jù)需要,劃分為不同的邏輯分區(qū),每個(gè)分區(qū)再用數(shù)字編號(hào)。比如我們前面多次用到的 /dev/sda ,還可以分成兩個(gè)分區(qū) /dev/sda1 和 /dev/sda2。
另一個(gè)比較常用的架構(gòu),是把多塊磁盤組合成一個(gè)邏輯磁盤,構(gòu)成冗余獨(dú)立磁盤陣列,也就是 RAID(Redundant Array of Independent Disks),從而可以提高數(shù)據(jù)訪問的性能,并且增強(qiáng)數(shù)據(jù)存儲(chǔ)的可靠性。
根據(jù)容量、性能和可靠性需求的不同,RAID 一般可以劃分為多個(gè)級(jí)別,如 RAID0、RAID1、RAID5、RAID10 等。
- RAID0 有最優(yōu)的讀寫性能,但不提供數(shù)據(jù)冗余的功能。
- 而其他級(jí)別的 RAID,在提供數(shù)據(jù)冗余的基礎(chǔ)上,對(duì)讀寫性能也有一定程度的優(yōu)化。
最后一種架構(gòu),是把這些磁盤組合成一個(gè)網(wǎng)絡(luò)存儲(chǔ)集群,再通過 NFS、SMB、iSCSI 等網(wǎng)絡(luò)存儲(chǔ)協(xié)議,暴露給服務(wù)器使用。
其實(shí)在 Linux 中,磁盤實(shí)際上是作為一個(gè)塊設(shè)備來管理的,也就是以塊為單位讀寫數(shù)據(jù),并且支持隨機(jī)讀寫。每個(gè)塊設(shè)備都會(huì)被賦予兩個(gè)設(shè)備號(hào),分別是主、次設(shè)備號(hào)。主設(shè)備號(hào)用在驅(qū)動(dòng)程序中,用來區(qū)分設(shè)備類型;而次設(shè)備號(hào)則是用來給多個(gè)同類設(shè)備編號(hào)。
通用塊層
跟我們上一節(jié)講到的虛擬文件系統(tǒng) VFS 類似,為了減小不同塊設(shè)備的差異帶來的影響,Linux 通過一個(gè)統(tǒng)一的通用塊層,來管理各種不同的塊設(shè)備。
通用塊層,其實(shí)是處在文件系統(tǒng)和磁盤驅(qū)動(dòng)中間的一個(gè)塊設(shè)備抽象層。它主要有兩個(gè)功能 。
第一個(gè)功能跟虛擬文件系統(tǒng)的功能類似。向上,為文件系統(tǒng)和應(yīng)用程序,提供訪問塊設(shè)備的標(biāo)準(zhǔn)接口;向下,把各種異構(gòu)的磁盤設(shè)備抽象為統(tǒng)一的塊設(shè)備,并提供統(tǒng)一框架來管理這些設(shè)備的驅(qū)動(dòng)程序。
第二個(gè)功能,通用塊層還會(huì)給文件系統(tǒng)和應(yīng)用程序發(fā)來的 I/O 請(qǐng)求排隊(duì),并通過重新排序、請(qǐng)求合并等方式,提高磁盤讀寫的效率。
其中,對(duì) I/O 請(qǐng)求排序的過程,也就是我們熟悉的 I/O 調(diào)度。事實(shí)上,Linux 內(nèi)核支持四種 I/O 調(diào)度算法,分別是 NONE、NOOP、CFQ 以及 DeadLine。這里我也分別介紹一下。
第一種 NONE ,更確切來說,并不能算 I/O 調(diào)度算法。因?yàn)樗耆皇褂萌魏?I/O 調(diào)度器,對(duì)文件系統(tǒng)和應(yīng)用程序的 I/O 其實(shí)不做任何處理,常用在虛擬機(jī)中(此時(shí)磁盤 I/O 調(diào)度完全由物理機(jī)負(fù)責(zé))
第二種 NOOP ,是最簡(jiǎn)單的一種 I/O 調(diào)度算法。它實(shí)際上是一個(gè)先入先出的隊(duì)列,只做一些最基本的請(qǐng)求合并,常用于 SSD 磁盤。
第三種 CFQ(Completely Fair Scheduler),也被稱為完全公平調(diào)度器,是現(xiàn)在很多發(fā)行版的默認(rèn) I/O 調(diào)度器,它為每個(gè)進(jìn)程維護(hù)了一個(gè) I/O 調(diào)度隊(duì)列,并按照時(shí)間片來均勻分布每個(gè)進(jìn)程的 I/O 請(qǐng)求
類似于進(jìn)程 CPU 調(diào)度,CFQ 還支持進(jìn)程 I/O 的優(yōu)先級(jí)調(diào)度,所以它適用于運(yùn)行大量進(jìn)程的系統(tǒng),像是桌面環(huán)境、多媒體應(yīng)用等。
最后一種 DeadLine 調(diào)度算法,分別為讀、寫請(qǐng)求創(chuàng)建了不同的 I/O 隊(duì)列,可以提高機(jī)械磁盤的吞吐量,并確保達(dá)到最終期限(deadline)的請(qǐng)求被優(yōu)先處理。DeadLine 調(diào)度算法,多用在 I/O 壓力比較重的場(chǎng)景,比如數(shù)據(jù)庫等。
I/O 棧
清楚了磁盤和通用塊層的工作原理,再結(jié)合上一期我們講過的文件系統(tǒng)原理,我們就可以整體來看 Linux 存儲(chǔ)系統(tǒng)的 I/O 原理了。
我們可以把 Linux 存儲(chǔ)系統(tǒng)的 I/O 棧,由上到下分為三個(gè)層次,分別是文件系統(tǒng)層、通用塊層和設(shè)備層。這三個(gè) I/O 層的關(guān)系如下圖所示,這其實(shí)也是 Linux 存儲(chǔ)系統(tǒng)的 I/O 棧全景圖。

根據(jù)這張 I/O 棧的全景圖,我們可以更清楚地理解,存儲(chǔ)系統(tǒng) I/O 的工作原理。
文件系統(tǒng)層,包括虛擬文件系統(tǒng)和其他各種文件系統(tǒng)的具體實(shí)現(xiàn)。它為上層的應(yīng)用程序,提供標(biāo)準(zhǔn)的文件訪問接口;對(duì)下會(huì)通過通用塊層,來存儲(chǔ)和管理磁盤數(shù)據(jù)。
通用塊層,包括塊設(shè)備 I/O 隊(duì)列和 I/O 調(diào)度器。它會(huì)對(duì)文件系統(tǒng)的 I/O 請(qǐng)求進(jìn)行排隊(duì),再通過重新排序和請(qǐng)求合并,然后才要發(fā)送給下一級(jí)的設(shè)備層。
設(shè)備層,包括存儲(chǔ)設(shè)備和相應(yīng)的驅(qū)動(dòng)程序,負(fù)責(zé)最終物理設(shè)備的 I/O 操作。
存儲(chǔ)系統(tǒng)的 I/O ,通常是整個(gè)系統(tǒng)中最慢的一環(huán)。所以, Linux 通過多種緩存機(jī)制來優(yōu)化 I/O 效率。
比方說,為了優(yōu)化文件訪問的性能,會(huì)使用頁緩存、索引節(jié)點(diǎn)緩存、目錄項(xiàng)緩存等多種緩存機(jī)制,以減少對(duì)下層塊設(shè)備的直接調(diào)用。
同樣,為了優(yōu)化塊設(shè)備的訪問效率,會(huì)使用緩沖區(qū),來緩存塊設(shè)備的數(shù)據(jù)。
小結(jié)
在今天的文章中,我們梳理了 Linux 磁盤 I/O 的工作原理,并了解了由文件系統(tǒng)層、通用塊層和設(shè)備層構(gòu)成的 Linux 存儲(chǔ)系統(tǒng) I/O 棧。
其中,通用塊層是 Linux 磁盤 I/O 的核心。向上,它為文件系統(tǒng)和應(yīng)用程序,提供訪問了塊設(shè)備的標(biāo)準(zhǔn)接口;向下,把各種異構(gòu)的磁盤設(shè)備,抽象為統(tǒng)一的塊設(shè)備,并會(huì)對(duì)文件系統(tǒng)和應(yīng)用程序發(fā)來的 I/O 請(qǐng)求進(jìn)行重新排序、請(qǐng)求合并等,提高了磁盤訪問的效率。
原文
https://time.geekbang.org/column/article/77010