日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

转载:谢谢原作者: 块设备驱动实战基础篇二 (继续完善170行过滤驱动代码至200行)

發(fā)布時(shí)間:2025/3/21 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 转载:谢谢原作者: 块设备驱动实战基础篇二 (继续完善170行过滤驱动代码至200行) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1.3塊設(shè)備驅(qū)動(dòng)關(guān)鍵數(shù)據(jù)結(jié)構(gòu)及函數(shù)API詳細(xì)剖析

經(jīng)過(guò)上節(jié)四個(gè)步驟我們已經(jīng)熟悉并實(shí)戰(zhàn)了一個(gè)最基本的過(guò)濾塊設(shè)備驅(qū)動(dòng)的設(shè)計(jì)技巧,我們這一節(jié)先不繼續(xù)實(shí)戰(zhàn),我們本節(jié)把上節(jié)170行代碼中接觸到的塊設(shè)備核心數(shù)據(jù)結(jié)構(gòu)和API接口剖析一下,把這部分掌握和理解一下。

?

我們把上節(jié)涉及的六個(gè)數(shù)據(jù)結(jié)構(gòu)和相關(guān)API接口羅列一下:

?塊設(shè)備核心數(shù)據(jù)結(jié)構(gòu)

gendisk

塊設(shè)備倉(cāng)庫(kù)

hd_struct

塊設(shè)備分區(qū)

block_device

文件系統(tǒng)層使用的塊設(shè)備描述符

request_queue

倉(cāng)庫(kù)的關(guān)卡(請(qǐng)求隊(duì)列)

request

包含多個(gè)bio的大請(qǐng)求

bio

單個(gè)請(qǐng)求

塊設(shè)備核心API接口

register_blkdev

注冊(cè)并申請(qǐng)門牌號(hào)

alloc_disk

申請(qǐng)倉(cāng)庫(kù)

blk_alloc_queue

申請(qǐng)倉(cāng)庫(kù)的關(guān)卡

blk_queue_make_request

注冊(cè)倉(cāng)庫(kù)的加工處理函數(shù)

add_disk

將申請(qǐng)的倉(cāng)庫(kù)注冊(cè)到內(nèi)核中,成為合法倉(cāng)庫(kù)

?

?

?

結(jié)合上節(jié)塊設(shè)備在Linux中的總體結(jié)構(gòu)圖來(lái)看,我們?cè)儋N一下這個(gè)圖,根據(jù)這個(gè)圖我們將請(qǐng)求從文件系統(tǒng)層構(gòu)建出bio開始,直到進(jìn)入到請(qǐng)求處理函數(shù),分析一下其過(guò)程,這個(gè)過(guò)程會(huì)擴(kuò)展描述到核心數(shù)據(jù)結(jié)構(gòu)中的幾個(gè)關(guān)鍵字段,大家先試著熟悉,然后我們會(huì)給出核心數(shù)據(jù)結(jié)構(gòu)的嵌套關(guān)系圖,讓大家更清楚的認(rèn)識(shí)一下各個(gè)核心數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系,最后我們會(huì)詳細(xì)剖析各個(gè)數(shù)據(jù)結(jié)構(gòu)和API接口功能。

?

?

?

上層文件系統(tǒng)發(fā)來(lái)I/O請(qǐng)求時(shí),我們?cè)趬K設(shè)備驅(qū)動(dòng)的請(qǐng)求處理函數(shù)make_request上接收到的是bio,每個(gè)bio結(jié)構(gòu)中都包含了一個(gè)bio_vec數(shù)組。bio_vec是用于記錄一段連續(xù)內(nèi)存空間位置信息的數(shù)據(jù)結(jié)構(gòu),包括描述這段內(nèi)存連續(xù)空間的頁(yè)指針描述符bv_page,數(shù)據(jù)長(zhǎng)度bv_len,數(shù)據(jù)在一個(gè)頁(yè)中的開始位置bv_offset。如此分析我們知道bio請(qǐng)求包含了一個(gè)bio_vec數(shù)組,意味著包含了一組內(nèi)存連續(xù)空間。

?

接下來(lái)文件系統(tǒng)層調(diào)用通用塊層的generic_make_request函數(shù),將請(qǐng)求插入到倉(cāng)庫(kù)的關(guān)卡即請(qǐng)求隊(duì)列上request_queue,如果隊(duì)列沒有使用,則繼續(xù)調(diào)用到我們注冊(cè)的請(qǐng)求處理函數(shù)make_request上。

?

請(qǐng)求隊(duì)列request_queue中的每一個(gè)元素是一個(gè)請(qǐng)求集合request,request包含了多個(gè)bio請(qǐng)求,同時(shí)多個(gè)request通過(guò)鏈表鏈接在一起,鏈表頭在request_queue上;同樣request中的多個(gè)bio也通過(guò)鏈表鏈接在一起。

?

磁盤描述符gendisk通過(guò)指向該磁盤的請(qǐng)求隊(duì)列的指針queue與其請(qǐng)求隊(duì)列關(guān)聯(lián)起來(lái)。內(nèi)核用結(jié)構(gòu)block_device代表一個(gè)塊設(shè)備對(duì)象,它是文件系統(tǒng)層使用的數(shù)據(jù)結(jié)構(gòu),如:整個(gè)硬盤或特定分區(qū)都是一個(gè)塊設(shè)備對(duì)象。如果該結(jié)構(gòu)代表一個(gè)分區(qū),則其成員bd_part指向設(shè)備的分區(qū)結(jié)構(gòu);如果該結(jié)構(gòu)代表設(shè)備,則其成員bd_disk指向設(shè)備的通用硬盤結(jié)構(gòu)gendisk。

?

?????? 根據(jù)上面的描述,我們把數(shù)據(jù)結(jié)構(gòu)關(guān)系畫一下,如下,大家可以更清楚的看一下各個(gè)結(jié)構(gòu)之間的關(guān)系,也更加能夠從整體上把握IO請(qǐng)求在操作系統(tǒng)內(nèi)核中的描述和處理。

?

?

?

?

?

根據(jù)上面這個(gè)圖,讓我們可以繼續(xù)總結(jié)一下,充分把握好它們的結(jié)構(gòu)關(guān)系。塊設(shè)備會(huì)有一個(gè)倉(cāng)庫(kù)描述gendisk,如果倉(cāng)庫(kù)有分區(qū),則分區(qū)由hd_struct描述,同時(shí)文件系統(tǒng)會(huì)對(duì)倉(cāng)庫(kù)及分區(qū)都用一個(gè)獨(dú)立的block_device進(jìn)行描述;文件系統(tǒng)產(chǎn)生bio請(qǐng)求,多個(gè)bio會(huì)組裝成一個(gè)request,多個(gè)request會(huì)組裝到request_queue請(qǐng)求隊(duì)列上。

?

好了,至此相信大家能夠很牢固的記住各結(jié)構(gòu)之間的關(guān)系了,并且能夠根據(jù)上圖從整體上把握好應(yīng)用層數(shù)據(jù)讀寫請(qǐng)求在操作系統(tǒng)內(nèi)核中的處理關(guān)系,下面我們?cè)敿?xì)剖析一下各個(gè)數(shù)據(jù)結(jié)構(gòu)及API的功能,大家可以作為一個(gè)參考,再后面實(shí)戰(zhàn)時(shí)可以繼續(xù)回來(lái)進(jìn)行查閱學(xué)習(xí)。

?

?

?? block_device關(guān)鍵成員剖析

類型

字段

說(shuō)明

dev_t

bd_dev

塊設(shè)備的主設(shè)備號(hào)和次設(shè)備號(hào)

struct inode*

bd_inode

指向bdev文件系統(tǒng)中塊設(shè)備對(duì)應(yīng)的文件索引節(jié)點(diǎn)的指針

int

bd_openers

計(jì)數(shù)器,統(tǒng)計(jì)塊設(shè)備已經(jīng)被打開了多少次

struct mutex

bd_mutex

打開或關(guān)閉的互斥量

struct list_head

bd_inodes

已打開的塊設(shè)備文件的索引節(jié)點(diǎn)鏈表的首部

void*

bd_holders

塊設(shè)備描述符的當(dāng)前所有者

struct block_device*

bd_contains

如果塊設(shè)備是一個(gè)分區(qū),則指向整個(gè)磁盤的塊設(shè)備描述符;否則,指向該塊設(shè)備描述符

unsigned

bd_block_size

塊大小

struct hd_struct*

bd_part

指向分區(qū)描述符的指針(如果該塊設(shè)備不是一個(gè)分區(qū),則為NULL)

unsigned

bd_part_count

計(jì)數(shù)器,統(tǒng)計(jì)包含在塊設(shè)備中的分區(qū)已經(jīng)被打開了多少次

struct gendisk*

bd_disk

指向塊設(shè)備中基本磁盤的gendisk結(jié)構(gòu)的指針

struct list_head

bd_list

用于塊設(shè)備描述符鏈表的指針

unsigned long

bd_private

指向塊設(shè)備持有者的私有數(shù)據(jù)的指針

?

?? hd_struct關(guān)鍵成員剖析

類型

字段

說(shuō)明

sector_t

start_sect

磁盤中分區(qū)的起始扇區(qū)

sector_t

nr_sects

分區(qū)的長(zhǎng)度(總共的扇區(qū)數(shù))

int

policy

如果分區(qū)是只讀的,則置為1;否則為0

int

partno

磁盤中分區(qū)的相對(duì)索引

?

?? gendisk關(guān)鍵成員剖析

類型

字段

說(shuō)明

int

major

磁盤主設(shè)備號(hào), 每個(gè)塊設(shè)備都有唯一的主設(shè)備號(hào),在這個(gè)塊設(shè)備上建立的分區(qū)都使用這個(gè)相同的主設(shè)備號(hào)。具有相同主設(shè)備號(hào)的設(shè)備,使用相同的驅(qū)動(dòng)程序。

int

first_minor

與磁盤關(guān)聯(lián)的第一個(gè)次設(shè)備號(hào)。在某一個(gè)設(shè)備上首先創(chuàng)建的設(shè)備的初始次設(shè)備號(hào)為0,在名稱中不顯示,如sda;在這個(gè)設(shè)備上依次建立的其他設(shè)備,此設(shè)備號(hào)在0基礎(chǔ)上依次加1,并在名稱中顯示,如sda1,sda2。

int

minors

與磁盤關(guān)聯(lián)的次設(shè)備號(hào)范圍。規(guī)定了可以在這個(gè)設(shè)備上創(chuàng)建多少個(gè)分設(shè)備(分區(qū))。當(dāng)次設(shè)備號(hào)數(shù)量是1時(shí),表示這個(gè)設(shè)備不能被分區(qū)。

char

disk_name

磁盤的標(biāo)準(zhǔn)命名(通常是相應(yīng)設(shè)備文件的規(guī)范名稱)

struct hd_struct

part0

磁盤的分區(qū)信息

const struct block_device_operations *

fops

指向塊設(shè)備操作函數(shù)集的指針

struct request_queue *

queue

指向磁盤請(qǐng)求隊(duì)列的指針

void *

private_data

塊設(shè)備驅(qū)動(dòng)程序的私有數(shù)據(jù)

int

flags

描述磁盤類型的標(biāo)志

?? 塊設(shè)備gendisk fops函數(shù)指針集

類型

方法

參數(shù)

觸發(fā)事件

int

(*open)

struct block_device*, fmode_t

打開塊設(shè)備文件,增加引用計(jì)數(shù)

int

(*release)

struct gendisk*, fmode_t

關(guān)閉對(duì)塊設(shè)備文件的最后一個(gè)引用,減少引用計(jì)數(shù)

int

(*ioctl)

struct block_device*, fmode_t, unsigned,unsigned long

在塊設(shè)備文件上發(fā)出ioctl()系統(tǒng)調(diào)用

?? request_queue請(qǐng)求隊(duì)列描述符中的關(guān)鍵字段

類型

字段

說(shuō)明

struct list_head

queue_head

待處理請(qǐng)求的鏈表

make_request_fn*

make_request_fn

設(shè)備驅(qū)動(dòng)程序的請(qǐng)求處理函數(shù)

?? request描述符的關(guān)鍵字段

類型

字段

說(shuō)明

struct list_head

queuelist

請(qǐng)求隊(duì)列鏈表的指針

struct bio*

bio

請(qǐng)求中第一個(gè)沒有完成傳送操作的bio,不能直接對(duì)該成員進(jìn)行訪問(wèn);而要使用rq_for_each_bio訪問(wèn)

struct bio*

biotail

請(qǐng)求鏈表中末尾的bio

?? bio結(jié)構(gòu)中的關(guān)鍵字段

類型

字段

說(shuō)明

sector_t

bi_sector

塊I/O操作的第一個(gè)磁盤扇區(qū)

struct bio*

bi_next

鏈接到請(qǐng)求隊(duì)列中的下一個(gè)bio

struct block_device *

bi_bdev

指向塊設(shè)備描述符的指針

unsigned long

bi_flags

bio的狀態(tài)標(biāo)志

unsigned long

bi_rw

I/O操作標(biāo)志

unsigned short

bi_vcnt

bio的bio_vec數(shù)組中段的數(shù)目

unsigned short

bi_idx

bio的bio_vec數(shù)組中段的當(dāng)前索引值

unsigned int

bi_phys_segments

合并之后bio中物理段的數(shù)目

unsigned int

bi_size

需要傳送的字節(jié)數(shù)

unsigned int

bi_seg_front_size

第一個(gè)可合并的段大小

unsigned int

bi_seg_back_size

最后一個(gè)可合并的段大小

unsigned int

bi_max_vecs

bio的bio_vec數(shù)組中允許的最大段數(shù)

struct bio_vec*

bi_io_vec

指向bio的bio_vec數(shù)組中的段的指針

atomic_t

bi_cnt

bio的引用計(jì)數(shù)

bio_end_io_t*

bi_end_io

bio的I/O操作結(jié)束時(shí)調(diào)用的方法

void*

bi_private

通用塊層和塊設(shè)備驅(qū)動(dòng)程序的I/O完成方法使用的指針

?? bio_vec結(jié)構(gòu)中的字段

類型

字段

說(shuō)明

struct page*

bv_page

指向段的頁(yè)框中頁(yè)描述符的指針

unsigned int

bv_len

段的字節(jié)長(zhǎng)度

unsigned int

bv_offset

頁(yè)框中中段數(shù)據(jù)的偏移量

?? 核心API函數(shù)

類型

函數(shù)名

輸入?yún)?shù)

返回值

說(shuō)明

?

int

register_blkdev

unsigned int major, const char* name

成功返回主設(shè)備號(hào),失敗返回一個(gè)負(fù)數(shù)。

向系統(tǒng)申請(qǐng)注冊(cè)一個(gè)名為“name”的主設(shè)備號(hào),當(dāng)主設(shè)備號(hào)設(shè)為0時(shí)由內(nèi)核自動(dòng)分配一個(gè)可用的主設(shè)備號(hào);若自己指定時(shí),需要確保不與已有設(shè)備沖突。

?

struct gendisk*

alloc_disk

int minors

成功返回一個(gè)指向gendisk描述符的指針,失敗返回NULL

分配一個(gè)gendisk結(jié)構(gòu),minors為次設(shè)備號(hào)的總數(shù),一般也就是磁盤分區(qū)的數(shù)量,為1時(shí)表示該設(shè)備不能被分區(qū),此后minors不能被修改

?

struct request_queue*

blk_alloc_queue

gfp_t gfp_mask

成功返回一個(gè)指向request_queue的指針,失敗時(shí)返回NULL

申請(qǐng)請(qǐng)求隊(duì)列,并給隊(duì)列分配空間,該隊(duì)列需要用戶自己去調(diào)用blk_queue_make_request函數(shù)進(jìn)行初始化,其中的make_request_fn函數(shù)也需要用戶自己實(shí)現(xiàn)

?

void

blk_queue_make_request

struct request_queue* q, make_request_fn* mfn

無(wú)返回

初始化一個(gè)設(shè)備的請(qǐng)求隊(duì)列,其參數(shù)make_request_fn需要我們自己實(shí)現(xiàn)

?

void

add_disk

struct gendisk *disk

無(wú)返回

將gendisk添加到系統(tǒng)中。調(diào)用該函數(shù)后,會(huì)在“/dev/”下顯示出塊設(shè)備名字,設(shè)備名字即是disk->disk_name的內(nèi)容。對(duì)add_disk()的調(diào)用必須發(fā)生在驅(qū)動(dòng)程序的初始化工作完成并能響應(yīng)磁盤的請(qǐng)求之后。

?

void

del_gendisk

struct gendisk* disk

無(wú)返回

將gendisk從系統(tǒng)中刪除。gendisk是一個(gè)引用計(jì)數(shù)結(jié)構(gòu),通常對(duì)del_gendisk的調(diào)用會(huì)刪除gendisk中的最終計(jì)數(shù),但是沒有機(jī)制能保證其肯定發(fā)生。因此當(dāng)調(diào)用此函數(shù)后,該結(jié)構(gòu)可能繼續(xù)存在(而且內(nèi)核可能會(huì)調(diào)用我們提供的各種方法)。

void

put_disk

struct gendisk* disk

無(wú)返回

釋放驅(qū)動(dòng)分配的gendisk結(jié)構(gòu)

void

unregister_blkdev

unsigned int major, const char *name

無(wú)返回

注銷驅(qū)動(dòng)程序,釋放申請(qǐng)的主設(shè)備號(hào)

void

blk_cleanup_queue

struct request_queue *q

無(wú)返回

釋放分配的請(qǐng)求隊(duì)列

void

bio_endio

struct bio *bio, int error

無(wú)返回

返回對(duì)bio請(qǐng)求的處理結(jié)果,第一個(gè)參數(shù)即為被處理的bio指針,第二個(gè)參數(shù)成功時(shí)為0,失敗時(shí)為-ERRNO。

?

?

?

1.4繼續(xù)完善170行代碼至200行 - 加入bio過(guò)濾功能

?

剛才我們已經(jīng)看到了一個(gè)真時(shí)的虛擬塊設(shè)備驅(qū)動(dòng),我們看看這個(gè)塊設(shè)備加載到系統(tǒng)中,linux內(nèi)核的IO棧發(fā)生了怎樣的變化。

?

?

?

看到有什么問(wèn)題嗎?對(duì)了,請(qǐng)求到我們這塊就結(jié)束了,為啥,通道被堵上了,那怎么打通呢?這是我們接下來(lái)增加30行代碼至200行,來(lái)把通道打開,注意僅僅是修改了make_request這個(gè)函數(shù),并且我們?cè)黾恿诵碌臇|東,請(qǐng)大家跟這我們進(jìn)一步學(xué)習(xí)。

?

?

?

??????

?????? 區(qū)別在于下半部,請(qǐng)求達(dá)到fbd_dev1和fbd_dev2后會(huì)繼續(xù)被過(guò)濾到更底層的塊設(shè)備/dev/sdb和/dev/sdc上,接下來(lái)我們看看這一步是如何在新增的30行代碼中做到的。

?

仍然是fbd_driver.c fbd_driver.h Makefile三個(gè)文件,Makefile文件內(nèi)容不變,我們不再貼出,然后頭文件我們先貼一下。

?

? 1#ifndef? _FBD_DRIVER_H

? 2#define? _FBD_DRIVER_H

? 3#include <linux/init.h>

? 4#include <linux/module.h>

? 5#include <linux/blkdev.h>

? 6#include <linux/bio.h>

? 7#include <linux/genhd.h>

? 8

? 9#define SECTOR_BITS???????????? (9)

?10#define DEV_NAME_LEN??????????? 32

?11

?12#define DRIVER_NAME????????????"filter driver"

?13

?14#define DEVICE1_NAME???????????"fbd1_dev"

?15#define DEVICE1_MINOR?????????? 0

?16#define DEVICE2_NAME???????????"fbd2_dev"

?17#define DEVICE2_MINOR?????????? 1

?18

?19struct fbd_dev {

?20???????? struct request_queue *queue;

?21????????struct gendisk *disk;

?22????????sector_t size;????????? /* devicesize in Bytes */

?23????????char lower_dev_name[DEV_NAME_LEN];

?24????????struct block_device *lower_bdev;

?25};

?26#endif

?

頭文件基本沒有變化,包括仍然包含5個(gè)基本頭文件,主要是新增加了數(shù)據(jù)結(jié)構(gòu)fbd_dev的三個(gè)成員,從22行到24行,如下:

?22???????? sector_t size;????????? /* device size in Bytes */

?23????????char lower_dev_name[DEV_NAME_LEN];

?24????????struct block_device *lower_bdev;

?

?????? size記錄將來(lái)要?jiǎng)?chuàng)建的fbd_dev設(shè)備的容量大小,在第一節(jié)中我們是定義了一個(gè)常量宏即512M,現(xiàn)在我們既然要加入BIO過(guò)濾功能,這個(gè)容量需要保持與fbd_dev底層設(shè)備大小一致,這個(gè)容量的獲取我們?cè)诤竺鏁?huì)分析到。然后是lower_dev_name記錄了底層設(shè)備的文件名字,lower_bdev則保存著底層設(shè)備的block_device描述符,這個(gè)指針的用處后面我們也會(huì)講解。

?

?

?

我們把完善后的fbd_driver.c再貼一下,來(lái)分析一下如果設(shè)計(jì)make_request函數(shù),讓請(qǐng)求可以通過(guò)我們的過(guò)濾塊設(shè)備后被提交到真正的底層設(shè)備上去,而不是直接在我們這一層返回退出。

?

? 1 /**

? 2 ?*?fbd-driver - filter block device driver

? 3?*? Author: Talk@studio

? 4 **/

? 5 #include "fbd_driver.h"

? 6

? 7 static int fbd_driver_major = 0;

? 8

? 9 static struct fbd_dev fbd_dev1 =

?10 {

?11????????.queue = NULL,

?12????????.disk = NULL,

?13 ????????.lower_dev_name = "/dev/sdb",

?14????????.lower_bdev = NULL,

?15????????.size = 0

?16 };

?17

?18 static struct fbd_dev fbd_dev2 =

?19 {

?20????????.queue = NULL,

?21????????.disk = NULL,

?22????????.lower_dev_name = "/dev/sdc",

?23????????.lower_bdev = NULL,

?24????????.size = 0

?25 };

?26

?27 static int fbddev_open(struct inode *inode,struct file *file);

?28 static int fbddev_close(struct inode*inode, struct file *file);

?29

?30 static struct block_device_operationsdisk_fops = {

?31?? ??????.open = fbddev_open,

?32????????.release = fbddev_close,

?33????????.owner = THIS_MODULE,

?34 };

?35

?36 static int fbddev_open(struct inode *inode,struct file *file)

37 {

?38????????printk("device is opened by:[%s]\n", current->comm);

?39????? ???return 0;

?40 }

?41

?42 static int fbddev_close(struct inode*inode, struct file *file)

?43 {

?44????????printk("device is closed by:[%s]\n", current->comm);

?45????????return 0;

?46 }

?47

?48 static int make_request(structrequest_queue *q, struct bio *bio)

?49 {

?50????????struct fbd_dev *dev = (struct fbd_dev *)q->queuedata;

?51????????printk("device [%s] recevied [%s] io request, "

?52???????????????? "access on dev sector[%llu], length is [%u] sectors.\n",

?53???????????????? dev->disk->disk_name,

?54???????????????? bio_data_dir(bio) == READ ?"read" : "write",

?55???????????????? bio->bi_sector,

?56???????????????? bio_sectors(bio));

?57

?58????????bio->bi_bdev = dev->lower_bdev;

?59????????submit_bio(bio_rw(bio), bio);

?60????????return 0;

?61 }

?62

?63 static int dev_create(struct fbd_dev *dev,char *dev_name, int major, int minor)

?64 {

?65????????int ret = 0;

?66

?67????????/* init fbd_dev */

?68????????dev->disk = alloc_disk(1);

?69????????if (!dev->disk) {

?70???????????????? printk("alloc diskerror");

?71???????????????? ret = -ENOMEM;

?72???????????????? goto err_out1;

73???????? }

?74

?75????????dev->queue = blk_alloc_queue(GFP_KERNEL);

?76????????if (!dev->queue) {

?77???????????????? printk("alloc queueerror");

?78?? ??????????????ret = -ENOMEM;

?79???????????????? goto err_out2;

?80????????}

?81

?82????????/* init queue */

?83????????blk_queue_make_request(dev->queue, make_request);

?84????????dev->queue->queuedata = dev;

?85

?86????????/* init gendisk */

?87? ???????strncpy(dev->disk->disk_name,dev_name, DEV_NAME_LEN);

?88????????dev->disk->major = major;

?89????????dev->disk->first_minor = minor;

?90????????dev->disk->fops = &disk_fops;

91

?92????????dev->lower_bdev = open_bdev_excl(dev->lower_dev_name, FMODE_WRITE| FMODE_READ, dev->lower_bdev);

?93????????if (IS_ERR(dev->lower_bdev)) {

?94???????????????? printk("Open thedevice[%s]'s lower dev [%s] failed!\n", dev_name, dev->lower_dev_name);

?95???????????????? ret = -ENOENT;

?96???????????????? goto err_out3;

?97????????}

?98

?99????????dev->size = get_capacity(dev->lower_bdev->bd_disk) <<SECTOR_BITS;

100

101???????? set_capacity(dev->disk,(dev->size >> SECTOR_BITS));

102

103???????? /* bind queue to disk */

104???????? dev->disk->queue = dev->queue;

105

106???????? /* add disk to kernel */

107???????? add_disk(dev->disk);

108???????? return 0;

109err_out3:

110???????? blk_cleanup_queue(dev->queue);

111err_out2:

112???????? put_disk(dev->disk);

113err_out1:

114???????? return ret;

115 }

116

117 staticvoid dev_delete(struct fbd_dev *dev, char *name)

118 {

119???????? printk("delete the device[%s]!\n", name);

120???????? close_bdev_excl(dev->lower_bdev);

121

122???????? blk_cleanup_queue(dev->queue);

123???????? del_gendisk(dev->disk);

124 ????????put_disk(dev->disk);

125 }

126

127 staticint __init fbd_driver_init(void)

128 {

129???????? int ret;

130

131???????? /* register fbd driver, get the drivermajor number*/

132???????? fbd_driver_major =register_blkdev(fbd_driver_major, DRIVER_NAME);

133???????? if (fbd_driver_major < 0) {

134???????????????? printk("get majorfail");

135???????????????? ret = -EIO;

136???????????????? goto err_out1;

137???????? }

138

139? ???????/* create the first device */

140???????? ret = dev_create(&fbd_dev1,DEVICE1_NAME, fbd_driver_major, DEVICE1_MINOR);

141???????? if (ret) {

142???????????????? printk("create device[%s] failed!\n", DEVICE1_NAME);

143???????????????? goto err_out2;

144???????? }

145

146???????? /* create the second device */

147???????? ret = dev_create(&fbd_dev2,DEVICE2_NAME, fbd_driver_major, DEVICE2_MINOR);

148???????? if (ret) {

149???????????????? printk("create device[%s] failed!\n", DEVICE2_NAME);

150???? ????????????goto err_out3;

151???????? }

152???????? return ret;

153err_out3:

154???????? dev_delete(&fbd_dev1,DEVICE1_NAME);

155err_out2:

156???????? unregister_blkdev(fbd_driver_major,DRIVER_NAME);

157err_out1:

158???????? return ret;

159 }

160

161 staticvoid __exit fbd_driver_exit(void)

162 {

163???????? /* delete the two devices */

164???????? dev_delete(&fbd_dev2,DEVICE2_NAME);

165???????? dev_delete(&fbd_dev1,DEVICE1_NAME);

166

167???????? /* unregister fbd driver */

168???????? unregister_blkdev(fbd_driver_major,DRIVER_NAME);

169???????? printk("block device driver exitsuccessfuly!\n");

170 }

171

172module_init(fbd_driver_init);

173module_exit(fbd_driver_exit);

174MODULE_LICENSE("GPL");

?

?

?????? 為了迅速看出fbd_driver.c相對(duì)于第一個(gè)版本的差異,大家可以比較一下,可以用我們?cè)谏蟽?cè)內(nèi)核編譯時(shí)介紹的diff 命令對(duì)比一下第一節(jié)中的fbd_driver.c和本節(jié)的fbd_driver.c有什么差異,我們把patch文件也貼一下,并繼續(xù)分析一下fbd_driver.c代碼。

?

Patch文件:

---fbd_driver_stage1/fbd_driver.c?????2013-02-25 22:45:23.000000000 -0800

+++fbd_driver_stage2/fbd_driver.c?????2013-02-26 19:45:05.000000000 -0800

@@ -6,8 +6,23 @@

?

?static int fbd_driver_major = 0;

?

-staticstruct fbd_dev fbd_dev1 = {NULL};

-staticstruct fbd_dev fbd_dev2 = {NULL};

+staticstruct fbd_dev fbd_dev1 =

+{

+?????? .queue = NULL,

+?????? .disk = NULL,

+?????? .lower_dev_name = "/dev/sdb",

+?????? .lower_bdev = NULL,

+?????? .size = 0

+};

+

+staticstruct fbd_dev fbd_dev2 =

+{

+?????? .queue = NULL,

+?????? .disk = NULL,

+?????? .lower_dev_name = "/dev/sdc",

+?????? .lower_bdev = NULL,

+?????? .size = 0

+};

?

?static int fbddev_open(struct inode *inode,struct file *file);

?static int fbddev_close(struct inode *inode,struct file *file);

@@ -40,7 +55,8 @@

??????????????? bio->bi_sector,

??????????????? bio_sectors(bio));

?

-?????? bio_endio(bio, bio->bi_size, 0);

+?????? bio->bi_bdev = dev->lower_bdev;

+?????? submit_bio(bio_rw(bio), bio);??

??????? return 0;

}

?

@@ -49,7 +65,6 @@

??????? int ret = 0;

?

??????? /* init fbd_dev */

-?????? dev->size = DEV_SIZE;

??????? dev->disk = alloc_disk(1);

??????? if (!dev->disk) {

??????????????? printk("alloc diskerror");

@@ -73,6 +88,16 @@

??????? dev->disk->major = major;

??????? dev->disk->first_minor = minor;

??????? dev->disk->fops = &disk_fops;

+??????

+?????? dev->lower_bdev =open_bdev_excl(dev->lower_dev_name, FMODE_WRITE | FMODE_READ, dev->lower_bdev);

+?????? if (IS_ERR(dev->lower_bdev)) {

+?????????????? printk("Open thedevice[%s]'s lower dev [%s] failed!\n", dev_name, dev->lower_dev_name);

+?????????????? ret = -ENOENT;?

+?????????????? goto err_out3;

+?????? }

+

+?????? dev->size = get_capacity(dev->lower_bdev->bd_disk)<< SECTOR_BITS;

+??????

??????? set_capacity(dev->disk,(dev->size >> SECTOR_BITS));

?

??????? /* bind queue to disk */

@@ -81,6+106,8 @@

??????? /* add disk to kernel */

??????? add_disk(dev->disk);

??????? return 0;

+err_out3:

+?????? blk_cleanup_queue(dev->queue);

?err_out2:

??????? put_disk(dev->disk);

?err_out1:

@@ -90,6 +117,8 @@

?static void dev_delete(struct fbd_dev *dev,char *name)

?{

??????? printk("delete the device[%s]!\n", name);

+?????? close_bdev_excl(dev->lower_bdev);

+

??????? blk_cleanup_queue(dev->queue);

??????? del_gendisk(dev->disk);

??????? put_disk(dev->disk);

?

首先看一下9-25行代碼,這里我們將fbd_dev1和fbd_dev2兩個(gè)設(shè)備的描述符初始化了一下,與第一版本不同,我們針對(duì)每個(gè)成員都進(jìn)行了賦值,如下:

?

? 9 static struct fbd_dev fbd_dev1 =

?10 {

?11????????.queue = NULL,

?12????????.disk = NULL,

?13????????.lower_dev_name = "/dev/sdb",

?14????????.lower_bdev = NULL,

?15????????.size = 0

?16 };

?17

?18 static struct fbd_dev fbd_dev2 =

?19 {

?20????????.queue = NULL,

?21????????.disk = NULL,

?22????????.lower_dev_name = "/dev/sdc",

?23????????.lower_bdev = NULL,

?24????????.size = 0

?25 };

?

以fbd_dev1為例,成員queue和disk指針依然賦值為NULL,表示還沒有為它們分配好數(shù)據(jù)結(jié)構(gòu),然后lower-_dev_name指定為”/dev/sdb”,表示fbd_dev1這個(gè)塊設(shè)備底層的塊設(shè)備是sdb設(shè)備,由此我們會(huì)想到,后續(xù)進(jìn)入fbd_dev1設(shè)備的請(qǐng)求,我們會(huì)將其過(guò)濾轉(zhuǎn)發(fā)到sdb上,在此我們通過(guò)簡(jiǎn)單靜態(tài)賦值的方式實(shí)現(xiàn),在后面我們的項(xiàng)目實(shí)戰(zhàn)訓(xùn)練中,我們會(huì)帶領(lǐng)大家實(shí)現(xiàn)如何完善我們的代碼做到靈活的動(dòng)態(tài)指定底層設(shè)備;lower_bdev指針也被賦值為NULL,后面我們就會(huì)講到如何獲取sdb設(shè)備的block_device描述符,最后size初始化為0。同樣fbd_dev2也進(jìn)行了一樣的初始化操作。通過(guò)初始化我們將fbd_dev1和fbd_dev2兩個(gè)設(shè)備各自綁定了其底層的設(shè)備,這部分也是為后續(xù)真正在IO路徑上實(shí)現(xiàn)請(qǐng)求過(guò)濾轉(zhuǎn)發(fā)做好了基本準(zhǔn)備,但是大家一定會(huì)清楚,現(xiàn)在的準(zhǔn)備工作其實(shí)還很簡(jiǎn)單,fbd_dev設(shè)備還沒有真正與sd#設(shè)備建立聯(lián)系,我們繼續(xù)往下分析。

?

?

直接看92-97行,這一段代碼完成了fbd_dev中l(wèi)ower_bdev和size成員的最終賦值操作,我們看到lower-_bdev指針通過(guò)調(diào)用open_bdev_excl函數(shù)獲得,該函數(shù)是內(nèi)核用于打開指定路徑的塊設(shè)備并返回block_device數(shù)據(jù)結(jié)構(gòu)指針的函數(shù),通過(guò)該函數(shù)我們獲取到了底層/dev/sd#設(shè)備的block_device地址,最終與sd#設(shè)備真正的建立了聯(lián)系,然后99行,通過(guò)調(diào)用get_capacity函數(shù)我們獲取到了底層設(shè)備的容量大小,然后在101行把該容量也設(shè)置進(jìn)fbd_dev設(shè)備的gendisk描述符中,至此過(guò)濾功能的準(zhǔn)備工作徹底完成。以上也是fbd_dev設(shè)備創(chuàng)建過(guò)程中新增加的處理邏輯。

?

?

然而設(shè)備創(chuàng)建好,還需在make_request請(qǐng)求處理函數(shù)上重新進(jìn)行了一下設(shè)計(jì),才能真正做到在IO路徑上對(duì)請(qǐng)求進(jìn)行過(guò)濾轉(zhuǎn)發(fā),我們看48-61行的make_request函數(shù)發(fā)生了哪些改變,如下,我們?cè)儋N一下。

?

?48 static int make_request(structrequest_queue *q, struct bio *bio)

?49 {

?50????????struct fbd_dev *dev = (struct fbd_dev *)q->queuedata;

?51????????printk("device [%s] recevied [%s] io request, "

?52???????????????? "access on dev sector[%llu], length is [%u] sectors.\n",

?53???????????????? dev->disk->disk_name,

?54???? ????????????bio_data_dir(bio) == READ ?"read" : "write",

?55???????????????? bio->bi_sector,

?56???????????????? bio_sectors(bio));

?57

?58????????bio->bi_bdev = dev->lower_bdev;

?59????????submit_bio(bio_rw(bio), bio);

?60????????return 0;

?61 }

?

?

關(guān)鍵的地方是58-59行,不像第一節(jié)的代碼,我們這里的請(qǐng)求處理函數(shù)完成了一個(gè)重要的功能,它把傳入?yún)?shù)bio重新修飾了,58這一行中我們把bio->bi_bdev賦值為底層設(shè)備的指針lower_bdev,而lower_bdev就是我們剛才介紹的在設(shè)備初始化中通過(guò)調(diào)用open_bdev_excl函數(shù)獲得的,也就是說(shuō)我們告訴請(qǐng)求的下一站地址去哪,聯(lián)系我們舉的圖書館的例子,請(qǐng)求就這樣被一層一層傳遞下去了,最后59行,我們調(diào)用請(qǐng)求提交函數(shù)把我們修飾完的請(qǐng)求繼續(xù)提交給底層的塊設(shè)備驅(qū)動(dòng)了,這個(gè)函數(shù)是submit_bio,不同于第一個(gè)版本中調(diào)用bio_endio直接掐掉請(qǐng)求終止結(jié)束,這里通過(guò)submit_bio繼續(xù)把請(qǐng)求轉(zhuǎn)發(fā)下去了。

?

?????? 至此我們的過(guò)濾塊設(shè)備驅(qū)動(dòng)真正做到了轉(zhuǎn)發(fā)請(qǐng)求,我們的驅(qū)動(dòng)程序終于具備了一個(gè)正常的塊設(shè)備的基本功能了,我們把bio在內(nèi)核棧中的流動(dòng)過(guò)程再畫一張圖描述一下。

?

?

?????? 我們趕緊試試吧,make完成后,我們加載fbd_driver.ko模塊看看,然后在/dev/下找到我們的設(shè)備文件fbd_disk,然后dd一下該設(shè)備是否可以正常進(jìn)行讀寫操作了,至此一個(gè)基本的塊設(shè)備驅(qū)動(dòng)我們已經(jīng)完成,接下來(lái)我們繼續(xù)介紹請(qǐng)求轉(zhuǎn)發(fā)過(guò)濾后到請(qǐng)求真正處理完成后的回調(diào)處理過(guò)程,進(jìn)一步完整的走完請(qǐng)求處理全過(guò)程。

總結(jié)

以上是生活随笔為你收集整理的转载:谢谢原作者: 块设备驱动实战基础篇二 (继续完善170行过滤驱动代码至200行)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。