在我們日常的認識中,磁盤是非常可靠的。作者03年自己組裝的電腦的磁盤現在還在用著,想想都快二十年了。很多人可能會想,消費級的磁盤都這么靠譜,那企業級的豈不是更加厲害。其實并非如此,其實磁盤是很容易出現故障的。
作者有幸參觀過一個大型的數據中心,發現成堆的故障磁盤。據說,該數據中心每天有幾十塊磁盤出現故障。谷歌在設計其分布式文件系統GFS的時候,也是將磁盤作為非常不可靠部件進行考慮的,因此在其架構中通過多副本保證其可靠性。
想想其中的原因,大概是使用頻度的差異造成的錯覺吧。我們平時使用的磁盤平均下來每天應該不會超過一小時,而且大部分時間都沒有數據讀寫。而數據中心磁盤則是7*24小時運行,且負載很重。試想,如果讓你7*24小時不歇著,估計...
好了,廢話少說。既然磁盤這么不靠譜,我們今天就給大家介紹一下存儲領域保證磁盤可靠性的技術。為了方便大家理解和日后學習,我們就以Linux內核為例進行介紹,其實技術是相通的,大家可以遷移過去。在Linux操作系統下提升磁盤可靠性的技術就是RAID技術。具體實現有兩種,一種是通過MD實現的多磁盤設備,另外一種是LVM。今天我們主要介紹一下通過MD實現的RAID技術。
一、RAID的原理及基本操作
人類認識事物的客觀規律是具體的比抽象的容易理解。因此,為了讓大家更加容易理解本文介紹的內容,我們先從具體的內容開始。
1. RAID的基本原理
RAID的全稱為廉價冗余磁盤陣列(Redundant Array of Inexpensive Disks),從字面可以看出其基本原理就是通過廉價的磁盤組成一組磁盤,從而提高磁盤的整體可靠性。在Linux操作系統層面,其實就是將物理磁盤通過軟件抽象為邏輯磁盤。以RAID1(兩塊磁盤存儲相同的數據,在出現一塊磁盤故障的情況下,數據不丟失)為例,通過Linux內核中的軟件創建一個虛擬的塊設備,而該塊設備中記錄了底層對應的物理設備及相關參數。
圖1 RAID示意圖
因此,從用戶層面來看就是一塊普通的磁盤設備,而在底層卻是2個獨立的物理硬盤。當用戶向邏輯磁盤寫數據的時候,其中的軟件會通過參數進行計算,并將數據重新定向到底層的物理設備。通過這種方法可以保證即使出現某個物理磁盤損壞,用戶的數據仍然完好無損。
2. Linux下RAID管理
在Linux操作系統下可以通過mdadm工具非常方便的創建RAID。我們以RAID1為例演示一下如何創建。可能很多同學沒有多塊物理磁盤,其實沒有關系,我們可以通過虛擬機創建虛擬磁盤或者loop設備模擬的方式創建RAID。
為了方便大家練習,本文就通過loop設備模擬磁盤來創建RAID。首先需要創建2個loop設備。具體執行如下命令:
圖2 創建loop設備
成功運行上述命令后,在/dev目錄下就多出2個設備,分別是loop0和loop1。我們可以將這兩個設備當做磁盤來使用。下面我們就可以創建RAID了,非常簡單,通過一條命令就可以了。
mdadm?--create?/dev/md1?--level=1?--raid-devices=2?/de?
在上面命令中/dev/md1表示創建的新設備的名稱,level=1表示是RAID1,后面分別是物理設備的數量和具體的物理設備路徑。
除了創建RAID之外,mdadm還支持很多功能,比如獲取RAID的詳細信息(mdadm --detail /dev/md1)。
圖3 RAID的詳細信息
該命令的功能很多,我們就不一一介紹了。這里只是拋磚引玉,具體內容大家自行學習就行。后面我們重點從原理和實現層面介紹一下Linux的RAID技術。
3. RAID軟件架構
Linux的RAID實現在用戶態和內核態都有涉及。其中用戶態主要進行RAID的管理,而內核態一方面配合用戶態進行RAID管理,另外一方面則實現對IO的處理,這部分才是RAID最為核心的內容。
圖4 軟件架構
對于基于SCSI物理磁盤的RAID來說,Linux環境下整個軟件架構如圖4所示。其中虛線以上的為用戶態的軟件模塊,虛線以下的為內核態的軟件模塊。這里比較核心的是RAID公共層,在這里主要創建md設備,該設備是一個邏輯設備,也是用戶可以看到的RAID設備。其下則是具體的RAID模塊,用于實現不同的RAID級別(算法)。
再往下就是通用SCSI驅動層了,也就是圖中的SCSI磁盤驅動這一層的內容。該層其實是SCSI系統的上層驅動(SCSI子系統分為上中下三層)。RAID模塊通過調用該層的數據訪問接口就可以實現物理磁盤數據讀寫了。
這里需要說明的是,這里的物理磁盤并不一定是本地磁盤。由于基于SAN或者其它協議的磁盤可以通過光纖或者SAS線連接到主機,并呈現為物理硬盤。這種物理硬盤與本地物理硬盤沒有任何差異。
二、RAID的代碼淺析
針對Linux內核的具體實現,我們簡單介紹一下其中的代碼。關于代碼部分我們以RAID1為例介紹兩部分的內容,一部分是關于創建RAID的邏輯;另一部分是請求處理邏輯。理解了上述內容,也就理解了關于RAID代碼邏輯的大部分內容。
1. 關于RAID的超級塊
接觸過Linux文件系統的同學應該對超級塊不會陌生。在RAID中也有超級塊(superblock),并且作用與文件系統類似。RAID超級塊的作用類似,可以將超級塊理解稱為RAID的地圖。RAID軟件對底層物理磁盤的一切操作都以該超級塊為依據。
Linux的RAID有多個版本,包括0.9、1.0、1.1和1.2四個版本,且版本之間并不能保證兼容性。對于1.2版本的RAID,其超級塊位于開始4KB偏移的位置。我們可以通過dd或者其它工具將該數據導出到文件中,并通過二進制工具查看。
圖5 RAID的超級塊
如圖5是作者導出的上面創建的RAID1的超級塊信息及數據結構(mdp_superblock_1)對比圖。如果看不清楚也沒關系,大家可以自行獲取上述進行,并對比。
RAID的超級塊內容非常多,在本文不可能一一介紹。這里大家只要知道這里面包含創建時間、RAID級別、設備大小及同步信息等內容即可。后續我們可能會專門介紹超級塊中每個成員的具體作用。
2. 創建RAID核心流程
創建RAID的流程是由用戶態觸發,內核態具體完成的。RAID的創建核心分為3個步驟,具體如下:
用戶態工具mdadm根據參數構建超級塊,并寫入物理設備
用戶態工具觸發創建md設備(RAID設備)
用戶態工具觸發內核,是RAID處于運行狀態
其中第一步我們不再解釋,原理很簡單,大家自行閱讀代碼即可。關于第二步,用戶態工具(mdadm)通過向/sys/module/md_mod/parameters/new_array中寫入一個名為md*的字符串來觸發內核創建md設備。
圖6 RAID創建流程
這里的核心是分配一個關于md設備的數據結構(mddev),并且調用通用塊層的接口創建一個通用塊設備并添加(add_disk)到系統。成功之后,我們就可以在/dev目錄下看到我們想創建的md設備了,設備名稱就是mdadm傳入的參數。這里面需要重點關注的是,在分配md設備的數據結構的時候會關聯一個名為md_make_request的函數,該函數就是RAID的IO處理函數。
此時已經可以看到設備,但是還不能使用,因為RAID設備還沒有與底層的物理設備關聯起來。因此,后續mdadm工具會通過系統調用觸發內核啟動RAID,具體流程如圖7所示。
圖7 RAID啟動流程
此時,內核會根據超級塊信息執行若干動作,并且更改其中某些狀態標記。成功之后RAID設備就可以使用了。關于細節我們這里不做介紹,介紹了大家也記不住,有興趣的同學可以自行閱讀代碼。
3. 讀寫請求核心流程
前文我們知道創建RAID其實是創建了一個通用塊設備,并注冊了請求處理函數(md_make_request)。當在用戶態通過文件系統接口訪問該塊設備(RAID)時,虛擬文件系統會調用該函數(請參考本號之前關于SCSI磁盤的文章)。因此,關于RAID的讀寫流程,我們就從該函數開始介紹,下面先看一下整體流程。
圖8 讀寫流程
從圖8可以看出整個流程還是比較簡單的。在公共層會根據參數調用個性層的接口,對于RAID1來說就是調用raid1_make_request函數。該函數中會根據請求類型出現不同的分支,上圖是寫數據的流程。
RAID1本身比較簡單(請求分別放入底層物理磁盤),IO經過簡單處理后會放入一個隊列中。然后喚醒守護線程刷寫隊列。
評論