歡迎您光臨本站 註冊首頁

用"RAID 0.0999" 防寫 Linux 硬碟分區

←手機掃碼閱讀     火星人 @ 2014-03-24 , reply:0

  Linux Kernel 中軟體 RAID 技術的實現

  計算機科學家 David Wheeler 有一句名言:"計算機科學中的任何問題,都可以通過加上一層邏輯層來解決。"這個原則在 Linux Kernel 中的許多場合被廣泛的應用。其中最為大家所熟知的應用,恐怕要算是在文件系統的層次上。Linux Kernel 為了能支持多種不同的文件系統,採取的解決辦法,就是在 Kernel 和文件系統之間添加一個邏輯層,也即所謂的 VFS(Virtual Filesystem Switch)。VFS 把來自 Kernel 的請求轉發給各個不同的文件系統驅動進行處理。這些文件系統驅動,有的實現的是傳統的硬碟分區上的文件系統,有的實現的是基於網路的分散式文件系統,甚至還有一些更加富有想象力和實驗性質的文件系統驅動。Linux 上豐富多彩的文件系統的實現都得益於 VFS 這一邏輯層的加入。

  Linux Kernel 中軟體 RAID 技術的實現,同樣體現出了計算機科學家 David Wheeler 的這一原則。不過,我們首先要看到的是這一原則在 Linux Kernel 中的 Block 設備的實現上的應用。

  Linux Kernel 支持多種 Block 設備,比如基於 IDE 匯流排或者是 SCSI 匯流排的硬碟、光碟驅動器和磁帶機,又比如在一個普通文件上實現的 Loopback Block 設備,或者是基於 TCP 的網路 Block 設備,甚至是基於內存的 RAM-DISK。這個對多種 Block 設備的支持,其實就是 Block 設備層這一邏輯層在發揮作用。每一個具體的 Block 設備的驅動,都要向 Kernel 中的 Block 設備層註冊,並提供一組入口。這以後 Kernel 就可以通過這一組入口,訪問這個 Block 設備。Block 設備驅動的這一組入口的確切定義如下。
CODE:
struct block_device_operations {
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
int (*check_media_change) (kdev_t);
int (*revalidate) (kdev_t); struct module *owner;
};

  一個 Block 設備驅動向負責的邏輯層註冊了這一組入口之後,系統用一個號碼,也即所謂的 Major Number,標識這一 Block 設備。用戶空間的程序通過訪問文件系統上的 /dev 目錄獲知某一 Block 設備和 Major Number 之間的對應關係。Kernel 則通過這一 Major Number 區分不同的設備,把來自用戶空間的請求轉發給相應的設備驅動。在這裡,Major Number 實際上擔任了 Block 設備層這一邏輯層的轉換開關這一角色。相應的,我們知道,在文件系統的 VFS 邏輯層上擔任轉換開關角色的則是文件系統的 Mount 路徑。

  前面簡單地講述了 Linux Kernel 中的 Block 設備。而 RAID 技術,我們知道,是利用多個 Block 設備來模擬一個單個的 Block 設備,並且,在這個模擬的過程中,提供一定程度的數據冗餘,以保護用戶數據不會因為某個單一的 Block 設備的故障而完全損毀。根據技術規範的細節的不同,RAID 又被分為幾個級別。RAID 0 並不提供任何程度的數據冗餘,而只是把單個硬碟 Block 設備的負載平均分擔到參加 RAID 0 的所有硬碟 Block 設備上,這在一定程度上增加了對硬碟上的用戶數據進行訪問的速度。而 RAID 4,5 都提供了相當程度的數據冗餘,可以保護用戶數據不會因為某個單個硬碟 Block 設備的故障而損毀。關於 Linux Kernel 中軟體 RAID 技術的原理的介紹,讀者可以參考 IBM developerWorks 上面的 Daniel Robbins 的相關文章,以及 Linux Documentation Project 上面的 The Software-RAID HOWTO。相關鏈接,請參見文後所列參考文獻。

  本文所關心的,是這些不同級別的軟體 RAID 技術在 Linux Kernel 中的具體實現辦法。在這裡,我們看到 Linux Kernel 向上面說到的 Block 設備層這一邏輯層,註冊了一個特殊的 Block 設備,稱之為 Multi-Disk Block Device(md)。可以說,這個 Block 設備形成了又一個邏輯層,來支持不同級別的 RAID 技術,甚至還可以支持其它的多硬碟 Block 設備,比如 LVM,邏輯卷控制等。我們看到,為了支持不同級別的 RAID 技術而引進的這又一個邏輯層,並沒有妨礙其它 Block 設備。其它的那些不牽扯到多硬碟技術的 Block 設備,並不需要通過 md 這一個邏輯層,這保證了這些設備的效率不會受到影響。

  自然而然,我們就要問道,在 md 這一邏輯層上擔任轉換開關角色的是誰呢?並且,我們還要問,各種級別的軟體 RAID,是如何向這一邏輯層進行註冊的呢?

  首先回答第一個問題,在 md 這一邏輯層上擔任轉換開關角色的是 Block 設備的 Minor Number。用戶程序從文件系統上的 /dev 目錄下找到一個設備名,隨之就找到了一個 Major Number 以及一個 Minor Number。而 Linux Kernel 就根據 Major Number 找到 Block 設備的驅動,在我們這裡,就是 md 多硬碟設備。然後 md 邏輯層根據 Minor Number 再往下找。找什麼呢?找不同的 Personality,這是 Linux Kernel 中的 md 邏輯層所使用的術語,它的具體定義如下。
CODE:
struct mdk_personality_s {
char *name;
int (*make_request) (mddev_t *mddev, int rw, struct buffer_head * bh);
int (*run) (mddev_t *mddev);
int (*stop) (mddev_t *mddev);
int (*status) (char *page, mddev_t *mddev);
int (*error_handler) (mddev_t *mddev, kdev_t dev);
int (*diskop) (mddev_t *mddev, mdp_disk_t **descriptor, int state);
int (*stop_resync) (mddev_t *mddev);
int (*restart_resync) (mddev_t *mddev);
int (*sync_request) (mddev_t *mddev, unsigned long block_nr);
};

  從上面的簡略介紹,我們看到, Linux Kernel 引入 Block 設備層這一邏輯層,來支持多種不同的 Block 設備驅動的實現。然後,在 Block 設備層下面又引入 md 設備層這一較小的邏輯層,來實現對多個級別的 RAID 技術的支持。在這兩個層次中擔任轉換開關角色的,分別是 Block 設備的 Major Number 和 Minor Number。

  用戶數據的走向

  接下來,我們再從另一個角度,來看一看 Linux Kernel 中的軟體 RAID 技術的實現。在這裡,我們要關注的是在 Linux Kernel 中,用戶數據的移動路徑。

  比如說要讀一個用戶文件。首先,用戶程序傳遞給 Kernel 一個路徑名,比如 /etc/passwd。然後,Kernel 中的 VFS 邏輯層根據當時它記錄下來的文件系統的 Mount 的情況,找到相應的文件系統驅動,就指揮它去找用戶程序所請求的文件。

  對於我們這裡所要關心的基於硬碟的傳統的文件系統來說,硬碟呈現在它面前的樣子就是連續的一長串的 Blocks。所以,對於硬碟文件系統來說,它所要做的,就是要根據用戶程序所請求的文件的路徑名,找到相對應的若干個 Blocks,這樣以後,它的任務就算完成了,接下來的事情,就是屬於硬碟 Block 設備驅動的了。對於傳統的硬碟文件系統來說,它們通過在硬碟分區的開頭的固定位置上,維持一些索引信息,也即所謂的 Meta Information,來完成這一任務。對於 Linux 上比較流行的 Ext2/3 文件系統的硬碟布局的詳細介紹,請參見作者在 IBM developerWorks 上的另一篇文章。相關鏈接,請參見文後的參考文獻。

  在文件系統之後,接下來登場的就是硬碟 Block 設備的驅動了。它所要完成的任務,就是在文件系統請求某一個 Block 中的數據的時候,把這個數據從硬碟上讀出來。我們看到,文件系統根據 VFS 中記錄的 Mount 信息,找到相關的設備,比如 /dev/hda1,或者是某一個硬碟 RAID 設備,告訴它,我要讀你上面的第幾個 Block 的數據。對於一個普通的硬碟 Block 設備驅動來說,它把這個 Block 號數翻譯成硬碟上的硬體地址,比如 IDE 設備的 C/H/S 地址或者是 LBA 地址,然後,把來自文件系統的請求轉發給 IDE 驅動就行了。

  而對於我們所關心的 RAID 技術來說,它要在把來自文件系統的請求轉發給底層的驅動(比如 IDE 驅動)之前,多做一些翻譯工作。這個翻譯工作,把呈現在文件系統面前的一長串的 Blocks 這一形象,翻譯到多個硬碟 Block 設備上面。這個翻譯工作,定義了 Linux Kernel 中的軟體 RAID 技術的實現。對於各個不同級別的 RAID 來說,它們各個採用了不同的翻譯技術。簡單的 RAID 0,就是把這一長串的 Blocks,均勻的分配到各個硬碟 Block 設備上面。而 RAID 4 和 5 採用的翻譯手段,就更為複雜一點。

  從上面的分析,我們看到,在整個過程中只有少量的索引數據在各層驅動之間傳遞,只是到了最後,才出現用戶數據的轉移,這對整個系統的性能是非常有利的。

  給硬碟分區加上一層防寫膜

  這層保護膜,其實就是一個清空了的硬碟分區。每一個受到保護的硬碟分區,對應有一個保護膜。在每次系統重新啟動之後,這層保護膜都會被清空。另外,在內存中,每個保護膜都維持了一個關於被保護的硬碟分區的 Blocks 的 Bitmap,每一個 Block 對應於 Bitmap 中的一個 Bit。每次啟動以後,Bitmap 都被全部置為 0。

  保護膜的驅動是在 RAID 0 的基礎上稍加改動得來的,我們稱之為 RAID 0.0999。它向 Linux Kernel 中的 md 邏輯層註冊了自己的 Personality,並負責維護自己的一份 Bitmap。當保護膜的驅動從文件系統接收到一個關於某個 Block 的讀請求的時候,它查閱自己維護的 Bitmap。如果相應的位為 0,它就從被保護的硬碟分區上讀取該 Block。如果相應的位為 1,它就從保護膜上讀取相應的 Block。當保護膜的驅動從文件系統接收到一個關於某個 Block 的寫請求的時候,它把自己維護的 Bitmap 的相應位置位為 1,並把數據寫入保護膜上相應的 Block 中。這樣就有效的實現了對相應的硬碟分區的防寫。

  RAID 0.0999 有些什麼用處呢?一個用處是對於在光碟上"實地"運行的 Linux 操作系統,由於光碟不可避免的是只讀的,那麼 RAID 0.0999 加上基於內存的 Block 設備 RAM-DISK 的支持,就可以提供一個可讀可寫的文件系統,使得在光碟上"實地"運行 Linux 成為一件簡單的事情。另外一個用處是對於操作系統的安全方面,由於保證了一份完整的用戶數據是只讀的,而每次系統重新啟動之後,保護膜又都被擦除乾淨,受系統崩潰和黑客入侵的影響,在一定程度上就被減小了。當然,RAID 0.0999 最主要的用處是作為本文作者的一個小玩具而已。

[火星人 ] 用"RAID 0.0999" 防寫 Linux 硬碟分區已經有513次圍觀

http://coctec.com/docs/linux/show-post-134532.html