塊設備也就是存儲以「塊」為單位數據的設備,比較典型的如磁碟設備、光碟或者優盤。本文首先集中在磁碟設備的相關內容的分析,其它設備類型很類似,暫時不做介紹。 在Windows操作系統下磁碟設備似乎是一個實實在在的設備,我們可以通過圖形界面對磁碟設備進行管理。如圖1是Windows下的磁碟管理界面,可以通過這個界面清晰的看到磁碟設備,並且可以對其進行格式化等操作。

圖1 Windows磁碟設備

Linux操作系統的磁碟設備並不直觀,在LInux系統中「一切皆文件」的理念下,磁碟設備其實也是一個文件,只不過是一個比較特殊的文件。如圖2是某些磁碟和分區的文件路徑,其中黃色字體部分是磁碟的路徑,而前面紅色方框內的b表示這個文件是磁碟設備文件,而非普通文件。

磁碟設備文件也是位於VFS(虛擬文件系統)下面,與Ext4等文件系統類似。用戶層面可以用訪問普通文件的介面(API)訪問磁碟。如下代碼是用Python實現的一個向磁碟寫入字元串的程序。代碼很簡單,就是打開磁碟所在的路徑(path),然後調用write函數寫數據。

import os, sys

def write_file(filename, data):
fd = file(filename, "a+")
fd.write(data)
fd.close()

write_file("/dev/sdb", "itworld123")

Linux系統中磁碟的本質

通過上面的描述我們知道對於Linux操作系統來說,磁碟就是一個文件。而磁碟本身就是一個線性存儲空間(可以理解為一個大數組),這種方式與文件也是非常類似的。鑒於上述相似性,Linux將磁碟設備抽象為一個文件並沒有任何不妥之處。 實質上,在Linux操作系統磁碟設備是基於一個稱為bdev的偽文件系統來管理的,bdev文件系統是一個在內存中的偽文件系統(在內存的文件系統,無持久化的數據),位置與Ext4等文件系統相同。如圖3所示,bdev文件系統的位置為圖中紅色區域。

理解了塊設備的管理方式,再結合我們之前對文件系統的相關介紹,這樣就很容易理解後續的內容了。在文件系統相關文章介紹中我們知道,不同文件系統數據處理的關鍵是其提供的函數集,而這個函數集是在打開文件的時候確定的。磁碟設備也是如此,當我們打開磁碟設備時,操作系統根據磁碟設備的特性,會初始化inode中的函數集。而後續對該磁碟設備的讀寫操作就能通過該函數集完成。如下代碼所示 ,塊設備連同字元設備和管道都作為特殊的文件進行處理,並初始化對應的函數集。

void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = rdev;
} else if (S_ISBLK(mode)) { /*塊設備函數集*/
inode->i_fop = &def_blk_fops;
inode->i_rdev = rdev;
} else if (S_ISFIFO(mode))
inode->i_fop = &pipefifo_fops;
... ...

完成函數集的初始化後,當用戶調用VFS層的介面是,VFS層就可以找到具體的處理函數,進而完成用戶的操作。這裡的函數集與本地文件系統的函數集別無二致,差異在於普通文件系統需要管理目錄和文件,而這裡是將磁碟看作一個大文件

磁碟的緩存

既然磁碟偽文件系統bdev本身也是一個文件系統,因此自然也可以有緩存。這個緩存就是用於提升磁碟性能的緩存系統。磁碟的緩存系統與文件系統的緩存系統類似,也是通過頁緩存來實現的。當然,Linux磁碟的緩存是可以關閉的,此時將調用另外一套函數集。 這樣說起來可能比較抽象,下面我們以一個具體的例子來看一下磁碟緩存的具體實現。如下是磁碟偽文件系統的函數集,我們以寫數據為例進行介紹。

寫數據的函數為blkdev_write_iter,該函數會調用generic_perform_write函數。如果大家閱讀過本號關於文件系統的文件的話,很清楚後者就是VFS中向頁緩存寫數據的函數。也就是說塊設備偽文件系統的邏輯與本地文件系統完全一致。

推薦閱讀:

相關文章