區塊裝置驅動比字元裝置複雜得多,涉及緩衝區管理、I/O 排程、請求佇列等核心機制。本文系統剖析 Linux 區塊裝置驅動架構、關鍵資料結構(block_device_operations、gendisk)、模組載入/卸載、open/release/ioctl、請求佇列處理流程,並以 RAMDISK 為實戰範例。
| 特性 | 字元裝置 | 區塊裝置 |
|---|---|---|
| 存取單位 | 位元組 | 固定大小區塊(通常 512 bytes) |
| 緩衝機制 | 通常無緩衝,直接讀寫 | 有緩衝區,可調整請求順序 |
| 存取模式 | 循序 | 隨機存取 |
| 效能優化 | 無 | 合併相鄰請求、電梯演算法 |
針對機械硬碟,重新排序請求(如 1→10→3→2 調整為 1→2→3→10)可大幅減少磁頭移動;但 SSD/RAMDISK 無機械延遲,排序效益有限。
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);
long (*unlocked_ioctl)(struct file *, unsigned, unsigned long);
long (*compat_ioctl)(struct file *, unsigned, unsigned long);
int (*direct_access)(struct block_device *, sector_t, unsigned long *);
int (*media_changed)(struct gendisk *); // 可移動媒體偵測
int (*revalidate_disk)(struct gendisk *); // 媒體變更後重新驗證
int (*getgeo)(struct block_device *, struct hd_geometry *); // 磁碟幾何資訊
struct module *owner;
};
struct gendisk {
int major; // 主裝置號
int first_minor; // 起始次裝置號
int minors; // 最大次裝置數(不可分區則為 1)
char disk_name[32]; // 裝置名稱
struct hd_struct **part; // 分區資訊
struct block_device_operations *fops;
struct request_queue *queue; // 請求佇列指標
void *private_data; // 私有資料
sector_t capacity; // 容量(以 512-byte sector 為單位)
// ... 其他欄位
};
同一磁碟的各分區共享 major,minor 不同。queue 是核心管理 I/O 請求的核心。
gendisk、設定 fops、queue、capacity、呼叫 add_disk()del_gendisk()、put_disk()、釋放請求佇列open/release:檢查媒體狀態、模組計數器ioctl:核心區塊層已處理大部分標準命令,驅動通常只需處理廠商特定命令request_queue 管理所有 I/O 請求,驅動需實作 request_fn 處理請求:
static void my_request_fn(struct request_queue *q) {
struct request *req;
while ((req = blk_fetch_request(q)) != NULL) {
// 處理 req->cmd_type == REQ_TYPE_FS 的讀寫請求
// 完成後呼叫 blk_end_request(req, 0, blk_rq_bytes(req));
}
}
關鍵 API:blk_init_queue()、blk_cleanup_queue()、blk_fetch_request()、blk_end_request()、blk_queue_logical_block_size()。
read()/write()bio 結構bio 形成 requestrequest_queue,觸發 request_fnblk_end_request()RAMDISK 將記憶體模擬為區塊裝置,無機械延遲,適合學習區塊驅動基本框架。核心步驟:
request_fn 直接在記憶體間 memcpygendisk、設定 capacity、queuegetgeo)完整程式碼請參考核心原始碼 drivers/block/brd.c。