linux塊設備驅動

    和字符驅動相比 ,在對磁盤,flash等設備進行讀寫時,塊設備驅動可以進行優化合並等操作,提高了操作效率。
    以下代碼來自linux-2.6.38。LDD3電子書網頁版的地址:http://oss.org.cn/kernel-book/ldd3/
一、註冊

這個任務的函數是 register_blkdev(在 <linux/fs.h> 中定義):

int register_blkdev(unsigned int major, const char *name)

參數是你的設備要使用的主編號和關聯的名子(內核將顯示它在 /proc/devices). 如果 major 傳遞爲0,

內核分配一個新的主編號並且返回它給調用者. 如常, 自 register_blkdev 的一個負的返回值指示已發生了一個錯誤.

取消註冊的對應函數是:

int unregister_blkdev(unsigned int major, const char *name);

這裏, 參數必須匹配傳遞給 register_blkdev 的那些, 否則這個函數返回 -EINVAL 並且什麼都不註銷.

在2.6內核, 對 register_blkdev 的調用完全是可選的. 由 register_blkdev 所進行的功能已隨時間正在減少;

這個調用唯一的任務是 (1) 如果需要, 分配一個動態主編號, 並且 (2) 在 /proc/devices 創建一個入口. 在將來的內核,

register_blkdev 可能被一起去掉. 同時, 但是, 大部分驅動仍然調用它; 它是慣例.

二、塊設備的操作

字符設備通過 file_ 操作結構使它們的操作對系統可用. 一個類似的結構用在塊設備上; 它是 struct block_device_operations, 定義在 <linux/fs.h>. 下面是一個對這個結構中的成員的簡短的概覽; 當我們進入 sbull 驅動的細節時詳細重新訪問它們.

int (*open)(struct inode *inode, struct file *filp);

int (*release)(struct inode *inode, struct file *filp);

就像它們的字符驅動對等體一樣工作的函數; 無論何時設備被打開和關閉都調用它們. 一個字符驅動可能通過啓動設備或者鎖住門(爲可移出的介質)來響應一個 open 調用. 如果你將介質鎖入設備, 你當然應當在 release 方法中解鎖.

int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

實現 ioctl 系統調用的方法. 但是, 塊層首先解釋大量的標準請求; 因此大部分的塊驅動 ioctl 方法相當短.

int (*media_changed) (struct gendisk *gd);

被內核調用來檢查是否用戶已經改變了驅動器中的介質的方法, 如果是這樣返回一個非零值. 顯然, 這個方法僅適用於支持可移出的介質的驅動器(並且最好給驅動一個"介質被改變"標誌); 在其他情況下可被忽略.

struct gendisk 參數是內核任何表示單個磁盤; 我們將在下一節查看這個結構.

int (*revalidate_disk) (struct gendisk *gd);

revalidate_disk 方法被調用來響應一個介質改變; 它給驅動一個機會來進行需要的任何工作使新介質準備好使用. 這個函數返回一個 int 值, 但是值被內核忽略.

struct module *owner;

一個指向擁有這個結構的模塊的指針; 它應當常常被初始化爲 THIS_MODULE.

專心的讀者可能已注意到這個列表一個有趣的省略: 沒有實際讀或寫數據的函數. 在塊 I/O 子系統, 這些操作由請求函數處理, 它們應當有它們自己的一節並且在本章後面討論. 在我們談論服務請求之前, 我們必須完成對磁盤註冊的討論.

1 struct block_device_operations {
 2     int (*open) (struct block_device *, fmode_t);
 3     int (*release) (struct gendisk *, fmode_t);
 4     int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
 5     int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
 6     int (*direct_access) (struct block_device *, sector_t,
 7                         void **, unsigned long *);
 8     unsigned int (*check_events) (struct gendisk *disk,
 9                       unsigned int clearing);
10     /* ->media_changed() is DEPRECATED, use ->check_events() instead */
11     int (*media_changed) (struct gendisk *);
12     void (*unlock_native_capacity) (struct gendisk *);
13     int (*revalidate_disk) (struct gendisk *);
14     int (*getgeo)(struct block_device *, struct hd_geometry *);
15     /* this callback is with swap_lock and sometimes page table lock held */
16     void (*swap_slot_free_notify) (struct block_device *, unsigned long);
17     struct module *owner;
18 };

三、gendisk 結構

struct gendisk (定義於 <linux/genhd.h>) 是單獨一個磁盤驅動器的內核表示。

 1 struct gendisk {
 2     /* major, first_minor and minors are input parameters only,
 3      * don't use directly.  Use disk_devt() and disk_max_parts().
 4      */
 5     int major;            /* major number of driver */
 6     int first_minor;
 7     int minors;                     /* maximum number of minors, =1 for
 8                                          * disks that can't be partitioned. */
 9 
10     char disk_name[DISK_NAME_LEN];    /* name of major driver */
11     char *(*devnode)(struct gendisk *gd, mode_t *mode);
12 
13     unsigned int events;        /* supported events */
14     unsigned int async_events;    /* async events, subset of all */
15 
16     /* Array of pointers to partitions indexed by partno.
17      * Protected with matching bdev lock but stat and other
18      * non-critical accesses use RCU.  Always access through
19      * helpers.
20      */
21     struct disk_part_tbl __rcu *part_tbl;
22     struct hd_struct part0;
23 
24     const struct block_device_operations *fops;
25     struct request_queue *queue;
26     void *private_data;
27 
28     int flags;
29     struct device *driverfs_dev;  // FIXME: remove
30     struct kobject *slave_dir;
31 
32     struct timer_rand_state *random;
33     atomic_t sync_io;        /* RAID */
34     struct disk_events *ev;
35 #ifdef  CONFIG_BLK_DEV_INTEGRITY
36     struct blk_integrity *integrity;
37 #endif
38     int node_id;
39 };
事實上, 內核還使用 gendisk 來表示分區, 但是驅動作者不必知道這點. struct gedisk 中有幾個成員, 必須被一個塊驅動初始化:
int major;
int first_minor;
int minors;
描述被磁盤使用的設備號的成員. 至少, 一個驅動器必須使用最少一個次編號. 如果你的驅動會是可分區的, 但是(並且大部分應當是), 你要分配一個次編號給每個可能的分區. 次編號的一個普通的值是 16, 它允許"全磁盤"設備盒 15 個分區. 一些磁盤驅動使用 64 個次編號給每個設備.
char disk_name[32];
應當被設置爲磁盤驅動器名子的成員. 它出現在 /proc/partitions 和 sysfs.
struct block_device_operations *fops;
來自前一節的設備操作集合.
struct request_queue *queue;
被內核用來管理這個設備的 I/O 請求的結構; 我們在"請求處理"一節中檢查它.
int flags;
一套標誌(很少使用), 描述驅動器的狀態. 如果你的設備有可移出的介質, 你應當設置 GENHD_FL_REMOVABLE. CD-ROM 驅動器可設置 GENHD_FL_CD. 如果, 由於某些原因, 你不需要分區信息出現在 /proc/partitions, 設置 GENHD_FL_SUPPRESS_PARTITIONS_INFO.
sector_t capacity;
這個驅動器的容量, 以512-字節扇區來計. sector_t 類型可以是 64 位寬. 驅動不應當直接設置這個成員; 相反, 傳遞扇區數目給 set_capacity.
void *private_data;
塊驅動可使用這個成員作爲一個指向它們自己內部數據的指針.
內核提供了一小部分函數來使用 gendisk 結構. 我們在這裏介紹它們, 接着看 sbull 如何使用它們來使系統可使用它的磁盤驅動器.
struct gendisk 是一個動態分配的結構, 它需要特別的內核操作來初始化; 驅動不能自己分配這個結構. 相反, 你必須調用:
struct gendisk *alloc_disk(int minors);
minors 參數應當是這個磁盤使用的次編號數目; 注意你不能在之後改變 minors 成員並且期望事情可以正確工作. 當不再需要一個磁盤時, 它應當被釋放, 使用:
void del_gendisk(struct gendisk *gd);
一個 gendisk 是一個被引用計數的結構(它含有一個 kobject). 有 get_disk 和 put_disk 函數用來操作引用計數, 但是驅動應當從不需要做這個. 正常地, 對 del_gendisk 的調用去掉了最一個 gendisk 的最終的引用, 但是不保證這樣. 因此, 這個結構可能繼續存在(並且你的方法可能被調用)在調用 del_gendisk 之後. 但是, 如果你刪除這個結構當沒有用戶時(即, 在最後的釋放之後, 或者在你的模塊清理函數), 你可確信你不會再收到它的信息.
分配一個 gendisk 結構不能使系統可使用這個磁盤. 要做到這點, 你必須初始化這個結構並且調用 add_disk:
void add_disk(struct gendisk *gd);
這裏記住一件重要的事情:一旦你調用add_disk, 這個磁盤是"活的"並且它的方法可被在任何時間被調用. 實際上, 這樣的第一個調用將可能發生, 即便在 add_disk 返回之前; 內核將讀前幾個字節以試圖找到一個分區表. 因此你不應當調用 add_disk 直到你的驅動被完全初始化並且準備好響應對那個磁盤的請求.

。。。。。。

發佈了52 篇原創文章 · 獲贊 16 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章