塊裝置驅動程式之一

2021-06-20 10:29:52 字數 3015 閱讀 6716

linux支援的兩種重要的裝置型別分別是字元裝置和塊裝置,塊裝置可以隨機地以固定大小的塊傳送資料。與字元裝置相比,塊裝置有以下幾個特殊之處:

塊裝置可以從資料的任何位置進行訪問

塊資料總是以固定長度進行傳輸,即便請求的這是乙個位元組

對塊裝置的訪問有大量的快取。當進行讀時,如果已經快取了,就直接使用快取中的資料,而不再讀裝置,對於寫也通過快取來進行延遲處理。

在塊系統中,資料塊指的是固定大小的資料,該固定大小由核心規定,通常是4096個位元組。與資料塊對應的是扇區,它是由裝置硬體所決定的乙個塊,其大小取決於硬體,常見的硬體的扇區大多都是512個位元組。資料塊的大小都是扇區大小的整數倍。

由於檔案多儲存在塊裝置上,而且現代作業系統使用虛擬記憶體來工作,為了實現虛擬記憶體就需要將資料在記憶體和儲存裝置(塊裝置)之間進行交換。因而高效的塊驅動對系統效能是至關重要的,塊驅動的框架要比字元裝置複雜的多,其中引入了偽檔案系統bdev,快取,預讀演算法,io排程器等,下圖是乙個簡單的圖示:

struct block_device被核心用來表示塊裝置,它是塊裝置的基礎資料結構。系統中所有的塊裝置都被新增到了偽檔案系統bdev中,虛擬檔案系統vfs通過該檔案系統來訪問塊裝置。bdev使用的核心資料結構為:

struct bdev_inode ;
由此可見,只要找到塊裝置在檔案系統中的inode(它被儲存在bdev_inode的vfs_inode域中),就可以找到該塊裝置對應的struct block_device,進而就可以對裝置進行操作了。從vfs到塊裝置的關聯關係就是通過偽檔案系統bdev建立起來的。

塊裝置的裝置檔案的file_operations結構中提供的是通用的塊讀寫函式,而不是驅動自己的讀寫。使用者發出的裝置讀寫是和緩衝區互動,也就是說使用者發出的讀寫請求讀寫的是緩衝區。塊框架負責(根據讀寫請求)完成緩衝區和物理裝置之間的資料交換。提交到裝置的操作都以請求的方式出現,所有的真實的裝置的讀寫都在請求被處理時被執行,因而塊裝置驅動最主要的工作就是完成請求的處理。

塊裝置框架提供了複雜的佇列功能用於對請求進行排隊,排程。請求佇列包含了一些基本的資訊,比如:

而請求則包含了本請求的基本資訊:

類似於字元裝置,對於塊裝置驅動來說,第一件事是向核心註冊自己。其對應的api為

int register_blkdev(unsigned int major, const char *name);
如果註冊時指定了主裝置號,則成功返回0,否則返回負的錯誤碼

如果註冊時指定的主裝置號為0,則成功會返回系統自動分配的主裝置號,失敗返回負的錯誤碼。

如果不再使用裝置,可以用以下api解除註冊:

int unregister_blkdev(unsigned int major, const char *name);
引數和註冊時的要匹配。

對於塊裝置來說,註冊實際上就完成了兩個動作:

如果沒有指定主裝置號,則分配乙個

將該裝置使用的裝置號資訊更新到資料庫major_names中,major_names中的資訊會出現在/proc/devices中

因此可以看出,註冊做的事情實際上非常少。註冊完成後,除了能夠在/proce/devices中看到裝置之外,不能對裝置做任何事情,裝置還無法使用。

將塊裝置註冊到系統中並不能使得塊裝置可用,而且其中並沒有涉及到我們提到的塊裝置的請求佇列,排程器等內容。塊裝置的這些複雜特性是由大量複雜的資料結構來實現的。

塊裝置的核心資料結構是struct block_device,其定義如下:

struct block_device ;
struct block_device用於向驅動程式呈現乙個塊裝置,而另外乙個資料結構struct gendisk則表示整個磁碟,乙個磁碟可能包括很多個struct block_device型別的塊裝置,其定義如下:

struct gendisk ;
分割槽資料結構struct hd_struct定義如下

struct hd_struct ;
其中關鍵資料域的含義如下:

struct gendisk的例項不能由驅動程式分配,必須使用api alloc_disk來分配,該api完成struct gendisk資料結構的分配和初始化,並呼叫裝置模型的api device_initialize完成裝置資料結構的初始化。在使用完後,必須使用del_gendisk來釋放它。

這三者的關係如圖所示

分割槽資訊中的__dev以及holder_dir將磁碟的分層資訊呈現到了kobject中。

字元裝置使用了file_operations作為底層所使用的操作函式集,但是塊裝置不再使用該結構,塊裝置使用的資料結構是struct block_device_operations,其定義如下:

struct block_device_operations  __do_const;
其中特殊的api的含義如下

通用磁碟資料結構中包含了該資料結構的例項。

請求佇列使用資料結構struct request_queue來表示,其定義如下

struct request_queue ;
該資料結構比較龐大,其主要成員及其含義如下:

該資料結構的其它一些域的含義參見注釋及其名字。

api blk_init_queue用於初始化乙個請求佇列,它的原型如下:

struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);
該函式會完成

blk_init_queue相當於先呼叫了blk_alloc_queue_node,後呼叫了blk_queue_make_request

裝置驅動 塊裝置驅動程式

塊裝置驅動程式提供對面向塊的裝置的訪問,這種裝置以隨機訪問的方式傳輸資料,並且資料總是具有固定大小的塊。典型的塊裝置是磁碟驅動器,也有其它型別的塊裝置。塊裝置和字元裝置有很大區別。比如塊裝置上可以掛載檔案系統,字元裝置不可以。這是隨機訪問帶來的優勢,因為檔案系統需要能按塊儲存資料,同時更需要能隨機讀...

塊裝置驅動程式

裝置描述 定義於linux genhd.h struct gendisk 裝置註冊 void add disk struct gendisk gd 裝置操作 字元裝置通過file operations結構來定義它所支援的操作。塊裝置通過struct block device operations結構...

塊裝置驅動程式

塊裝置驅動程式 1 塊裝置和字元裝置的區別 1,讀取資料的單元不同,塊裝置讀寫資料的基本單元式塊,字元裝置的基本單元是位元組。2,塊裝置可以隨機訪問,字元裝置只能順序訪問。2 linux核心中塊裝置的描述 struct gendisk 裝置操作 struct block device operati...