Linux设备驱动--块设备(二)之相关结构体
上回最后面介紹了相關數據結構,下面再詳細介紹
塊設備對象結構 block_device
內核用結構block_device實例代表一個塊設備對象,如:整個硬盤或特定分區。如果該結構代表一個分區,則其成員bd_part指向設備的分區結構。如果該結構代表設備,則其成員bd_disk指向設備的通用硬盤結構gendisk
當用戶打開塊設備文件時,內核創建結構block_device實例,設備驅動程序還將創建結構gendisk實例,分配請求隊列并注冊結構block_device實例。
塊設備對象結構block_device列出如下(在include/Linux/fs.h中)
[cpp]?view plaincopy print?
通用硬盤結構 gendisk
結構體gendisk代表了一個通用硬盤(generic hard disk)對象,它存儲了一個硬盤的信息,包括請求隊列、分區鏈表和塊設備操作函數集等。塊設備驅動程序分配結構gendisk實例,裝載分區表,分配請求隊列并填充結構的其他域。
支持分區的塊驅動程序必須包含 <linux/genhd.h> 頭文件,并聲明一個結構gendisk,內核還維護該結構實例的一個全局鏈表gendisk_head,通過函數add_gendisk、del_gendisk和get_gendisk維護該鏈表。
結構gendisk列出如下(在include/linux/genhd.h中):
[cpp]?view plaincopy print?
Linux內核提供了一組函數來操作gendisk,主要包括:
分配gendisk
struct gendisk *alloc_disk(int minors);
minors 參數是這個磁盤使用的次設備號的數量,一般也就是磁盤分區的數量,此后minors不能被修改。
增加gendisk
gendisk結構體被分配之后,系統還不能使用這個磁盤,需要調用如下函數來注冊這個磁盤設備:
void add_disk(struct gendisk *gd);
特別要注意的是對add_disk()的調用必須發生在驅動程序的初始化工作完成并能響應磁盤的請求之后。
?釋放gendisk
當不再需要一個磁盤時,應當使用如下函數釋放gendisk:
void del_gendisk(struct gendisk *gd);
設置gendisk容量
void set_capacity(struct gendisk *disk, sector_t size);
塊設備中最小的可尋址單元是扇區,扇區大小一般是2的整數倍,最常見的大小是512字節。扇區的大小是設備的物理屬性,扇區是所有塊設備的基本單元,塊設備 無法對比它還小的單元進行尋址和操作,不過許多塊設備能夠一次就傳輸多個扇區。雖然大多數塊設備的扇區大小都是512字節,不過其它大小的扇區也很常見, 比如,很多CD-ROM盤的扇區都是2K大小。不管物理設備的真實扇區大小是多少,內核與塊設備驅動交互的扇區都以512字節為單位。因此,set_capacity()函數也以512字節為單位。
分區結構hd_struct代表了一個分區對象,它存儲了一個硬盤的一個分區的信息,驅動程序初始化時,從硬盤的分區表中提取分區信息,存放在分區結構實例中。
塊設備操作函數集結構 block_device_operations
字符設備通過 file_operations 操作結構使它們的操作對系統可用. 一個類似的結構用在塊設備上是 struct block_device_operations,
定義在 <linux/fs.h>.?
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 方法相當短.
PS:在block_device_operations中沒有實際讀或寫數據的函數. 在塊 I/O 子系統, 這些操作由請求函數處理
請求結構request
結構request代表了掛起的I/O請求,每個請求用一個結構request實例描述,存放在請求隊列鏈表中,由電梯算法進行排序,每個請求包含1個或多個結構bio實例
[cpp]?view plaincopy print??request結構體的主要成員包括:
?sector_t hard_sector;?
unsigned long hard_nr_sectors;?
unsigned int hard_cur_sectors;?
上述3個成員標識還未完成的扇區,hard_sector是第1個尚未傳輸的扇區,hard_nr_sectors是尚待完成的扇區數,hard_cur_sectors是并且當前I/O操作中待完成的扇區數。這些成員只用于內核塊設備層,驅動不應當使用它們。
?sector_t sector;?
unsigned long nr_sectors;?
unsigned int current_nr_sectors;?
驅動中會經常與這3個成員打交道,這3個成員在內核和驅動交互中發揮著重大作用。它們以512字節大小為1個扇區,如果硬件的扇區大小不是512字節,則需要進行相應的調整。例如,如果硬件的扇區大小是2048字節,則在進行硬件操作之前,需要用4來除起始扇區號。
?hard_sector、hard_nr_sectors、hard_cur_sectors與sector、nr_sectors、current_nr_sectors之間可認為是“副本”關系。
struct bio *bio;?
bio是這個請求中包含的bio結構體的鏈表,驅動中不宜直接存取這個成員,而應該使用后文將介紹的rq_for_each_bio()。
請求隊列結構request_queue
每個塊設備都有一個請求隊列,每個請求隊列單獨執行I/O調度,請求隊列是由請求結構實例鏈接成的雙向鏈表,鏈表以及整個隊列的信息用結構request_queue描述,稱為請求隊列對象結構或請求隊列結構。它存放了關于掛起請求的信息以及管理請求隊列(如:電梯算法)所需要的信息。結構成員request_fn是來自設備驅動程序的請求處理函數。
請求隊列結構request_queue列出如下(在/include/linux/blk_dev.h中)
太長了,此處略,其實也看不懂,- -#
Bio結構
通常1個bio對應1個I/O請求,IO調度算法可將連續的bio合并成1個請求。所以,1個請求可以包含多個bio。
內核中塊I/O操作的基本容器由bio結構體表示,定義 在<linux/bio.h>中,該結構體代表了正在現場的(活動的)以片段(segment)鏈表形式組織的塊I/O操作。一個片段是一小 塊連續的內存緩沖區。這樣的好處就是不需要保證單個緩沖區一定要連續。所以通過片段來描述緩沖區,即使一個緩沖區分散在內存的多個位置上,bio結構體也 能對內核保證I/O操作的執行,這樣的就叫做聚散I/O.
bio為通用層的主要數據結構,既描述了磁盤的位置,又描述了內存的位置,是上層內核vfs與下層驅動的連接紐帶
內存數據段結構bio_vec
?????? 結構bio_vec代表了內存中的一個數據段,數據段用頁、偏移和長度描
述。I/O需要執行的內存位置用段表示,結構bio指向了一個段的數組。
結構bio_vec列出如下(在include/linux/bio.h中):
struct bio_vec {
?????? struct page???? *bv_page;?? /*數據段所在的頁*/
?????? unsigned short? bv_len;???? /*數據段的長度*/
?????? unsigned short? bv_offset;? /*數據段頁內偏移*/
};
塊設備各個結構體間關系
轉載于:https://www.cnblogs.com/Ph-one/p/6435916.html
總結
以上是生活随笔為你收集整理的Linux设备驱动--块设备(二)之相关结构体的全部內容,希望文章能夠幫你解決所遇到的問題。