Linux裝置驅動 塊裝置(二)之相關結構體

2021-09-08 17:42:26 字數 4243 閱讀 4913

上回最後面介紹了相關資料結構,下面再詳細介紹

核心用結構block_device例項代表乙個塊裝置物件,如:整個硬碟或特定分割槽。如果該結構代表乙個分割槽,則其成員bd_part指向裝置的分割槽結構。如果該結構代表裝置,則其成員bd_disk指向裝置的通用硬碟結構gendisk

當使用者開啟塊裝置檔案時,核心建立結構block_device例項,裝置驅動程式還將建立結構gendisk例項,分配請求佇列並註冊結構block_device例項。

塊裝置物件結構block_device列出如下(在include/linux/fs.h中)

[cpp]view plain

copy

print?

struct block_device ;  

結構體gendisk代表了乙個通用硬碟(generic hard disk)物件,它儲存了乙個硬碟的資訊,包括請求佇列、分割槽鍊錶和塊裝置操作函式集等。塊裝置驅動程式分配結構gendisk例項,裝載分割槽表,分配請求佇列並填充結構的其他域。

支援分割槽的塊驅動程式必須包含 標頭檔案,並宣告乙個結構gendisk,核心還維護該結構例項的乙個全域性鍊錶gendisk_head,通過函式add_gendisk、del_gendisk和get_gendisk維護該鍊錶。

結構gendisk列出如下(在include/linux/genhd.h中):

[cpp]view plain

copy

print?

struct gendisk ;  

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代表了乙個分割槽物件,它儲存了乙個硬碟的乙個分割槽的資訊,驅動程式初始化時,從硬碟的分割槽表中提取分割槽資訊,存放在分割槽結構例項中。

字元裝置通過 file_operations 操作結構使它們的操作對系統可用. 乙個類似的結構用在塊裝置上是 struct block_device_operations,

定義在 . 

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代表了掛起的i/o請求每個請求用乙個結構request例項描述,存放在請求佇列鍊錶中,由電梯演算法進行排序,每個請求包含1個或多個結構bio例項

[cpp]view plain

copy

print?

struct request ;  

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()。

每個塊裝置都有乙個請求佇列,每個請求佇列單獨執行i/o排程,請求佇列是由請求結構例項鏈結成的雙向鍊錶,鍊錶以及整個佇列的資訊用結構request_queue描述,稱為請求佇列物件結構或請求佇列結構。它存放了關於掛起請求的資訊以及管理請求佇列(如:電梯演算法)所需要的資訊。結構成員request_fn是來自裝置驅動程式的請求處理函式。

請求佇列結構request_queue列出如下(在/include/linux/blk_dev.h中)

太長了,此處略,其實也看不懂,- -#

通常1個bio對應1個i/o請求,io排程演算法可將連續的bio合併成1個請求。所以,1個請求可以包含多個bio。

核心中塊i/o操作的基本容器由bio結構體表示,定義 在中,該結構體代表了正在現場的(活動的)以片段(segment)鍊錶形式組織的塊i/o操作。乙個片段是一小 塊連續的記憶體緩衝區。這樣的好處就是不需要保證單個緩衝區一定要連續。所以通過片段來描述緩衝區,即使乙個緩衝區分散在記憶體的多個位置上,bio結構體也 能對核心保證i/o操作的執行,這樣的就叫做聚散i/o.

bio為通用層的主要資料結構,既描述了磁碟的位置,又描述了記憶體的位置,是上層核心vfs與下層驅動的連線紐帶

[cpp]view plain

copy

print?

struct bio ;  

結構bio_vec代表了記憶體中的乙個資料段,資料段用頁、偏移和長度描

述。i/o需要執行的記憶體位置用段表示,結構bio指向了乙個段的陣列。

結構bio_vec列出如下(在include/linux/bio.h中):

struct bio_vec ;

塊裝置各個結構體間關係

linux驅動之塊裝置驅動

塊裝置驅動的系統架構 塊裝置註冊過程 1,註冊裝置塊驅動程式 register blkdev 2,初始化請求佇列 blk init queue 3,指明扇區的大小 blk queue logical block size dev queue,sect size 4,申請乙個gendisk結構,初始化...

Linux裝置驅動 塊裝置(二)之相關結構體

上回最後面介紹了相關資料結構,下面再詳細介紹 核心用結構block device例項代表乙個塊裝置物件,如 整個硬碟或特定分割槽。如果該結構代表乙個分割槽,則其成員bd part指向裝置的分割槽結構。如果該結構代表裝置,則其成員bd disk指向裝置的通用硬碟結構gendisk 當使用者開啟塊裝置檔...

Linux裝置驅動 塊裝置(二)之相關結構體

上回最後面介紹了相關資料結構,下面再詳細介紹 核心用結構block device例項代表乙個塊裝置物件,如 整個硬碟或特定分割槽。如果該結構代表乙個分割槽,則其成員bd part指向裝置的分割槽結構。如果該結構代表裝置,則其成員bd disk指向裝置的通用硬碟結構gendisk 當使用者開啟塊裝置檔...