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

1.3块设备驱动关键数据结构及函数API详细剖析

经过上节四个步骤我们已经熟悉并实战了一个最基本的过滤块设备驱动的设计技巧,我们这一节先不继续实战,我们本节把上节170行代码中接触到的块设备核心数据结构和API接口剖析一下,把这部分掌握和理解一下。

我们把上节涉及的六个数据结构和相关API接口罗列一下:

 块设备核心数据结构

gendisk

块设备仓库

hd_struct

块设备分区

block_device

文件系统层使用的块设备描述符

request_queue

仓库的关卡(请求队列)

request

包含多个bio的大请求

bio

单个请求

块设备核心API接口

register_blkdev

注册并申请门牌号

alloc_disk

申请仓库

blk_alloc_queue

申请仓库的关卡

blk_queue_make_request

注册仓库的加工处理函数

add_disk

将申请的仓库注册到内核中,成为合法仓库

结合上节块设备在Linux中的总体结构图来看,我们再贴一下这个图,根据这个图我们将请求从文件系统层构建出bio开始,直到进入到请求处理函数,分析一下其过程,这个过程会扩展描述到核心数据结构中的几个关键字段,大家先试着熟悉,然后我们会给出核心数据结构的嵌套关系图,让大家更清楚的认识一下各个核心数据结构之间的关系,最后我们会详细剖析各个数据结构和API接口功能。

 

上层文件系统发来I/O请求时,我们在块设备驱动的请求处理函数make_request上接收到的是bio,每个bio结构中都包含了一个bio_vec数组。bio_vec是用于记录一段连续内存空间位置信息的数据结构,包括描述这段内存连续空间的页指针描述符bv_page,数据长度bv_len,数据在一个页中的开始位置bv_offset。如此分析我们知道bio请求包含了一个bio_vec数组,意味着包含了一组内存连续空间。

接下来文件系统层调用通用块层的generic_make_request函数,将请求插入到仓库的关卡即请求队列上request_queue,如果队列没有使用,则继续调用到我们注册的请求处理函数make_request上。

请求队列request_queue中的每一个元素是一个请求集合request,request包含了多个bio请求,同时多个request通过链表链接在一起,链表头在request_queue上;同样request中的多个bio也通过链表链接在一起。

磁盘描述符gendisk通过指向该磁盘的请求队列的指针queue与其请求队列关联起来。内核用结构block_device代表一个块设备对象,它是文件系统层使用的数据结构,如:整个硬盘或特定分区都是一个块设备对象。如果该结构代表一个分区,则其成员bd_part指向设备的分区结构;如果该结构代表设备,则其成员bd_disk指向设备的通用硬盘结构gendisk。

       根据上面的描述,我们把数据结构关系画一下,如下,大家可以更清楚的看一下各个结构之间的关系,也更加能够从整体上把握IO请求在操作系统内核中的描述和处理。

 

根据上面这个图,让我们可以继续总结一下,充分把握好它们的结构关系。块设备会有一个仓库描述gendisk,如果仓库有分区,则分区由hd_struct描述,同时文件系统会对仓库及分区都用一个独立的block_device进行描述;文件系统产生bio请求,多个bio会组装成一个request,多个request会组装到request_queue请求队列上。

好了,至此相信大家能够很牢固的记住各结构之间的关系了,并且能够根据上图从整体上把握好应用层数据读写请求在操作系统内核中的处理关系,下面我们详细剖析一下各个数据结构及API的功能,大家可以作为一个参考,再后面实战时可以继续回来进行查阅学习。

   block_device关键成员剖析

类型

字段

说明

dev_t

bd_dev

块设备的主设备号和次设备号

struct inode*

bd_inode

指向bdev文件系统中块设备对应的文件索引节点的指针

int

bd_openers

计数器,统计块设备已经被打开了多少次

struct mutex

bd_mutex

打开或关闭的互斥量

struct list_head

bd_inodes

已打开的块设备文件的索引节点链表的首部

void*

bd_holders

块设备描述符的当前所有者

struct block_device*

bd_contains

如果块设备是一个分区,则指向整个磁盘的块设备描述符;否则,指向该块设备描述符

unsigned

bd_block_size

块大小

struct hd_struct*

bd_part

指向分区描述符的指针(如果该块设备不是一个分区,则为NULL)

unsigned

bd_part_count

计数器,统计包含在块设备中的分区已经被打开了多少次

struct gendisk*

bd_disk

指向块设备中基本磁盘的gendisk结构的指针

struct list_head

bd_list

用于块设备描述符链表的指针

unsigned long

bd_private

指向块设备持有者的私有数据的指针

   hd_struct关键成员剖析

类型

字段

说明

sector_t

start_sect

磁盘中分区的起始扇区

sector_t

nr_sects

分区的长度(总共的扇区数)

int

policy

如果分区是只读的,则置为1;否则为0

int

partno

磁盘中分区的相对索引

   gendisk关键成员剖析

类型

字段

说明

int

major

磁盘主设备号, 每个块设备都有唯一的主设备号,在这个块设备上建立的分区都使用这个相同的主设备号。具有相同主设备号的设备,使用相同的驱动程序。

int

first_minor

与磁盘关联的第一个次设备号。在某一个设备上首先创建的设备的初始次设备号为0,在名称中不显示,如sda;在这个设备上依次建立的其他设备,此设备号在0基础上依次加1,并在名称中显示,如sda1,sda2。

int

minors

与磁盘关联的次设备号范围。规定了可以在这个设备上创建多少个分设备(分区)。当次设备号数量是1时,表示这个设备不能被分区。

char

disk_name

磁盘的标准命名(通常是相应设备文件的规范名称)

struct hd_struct

part0

磁盘的分区信息

const struct block_device_operations *

fops

指向块设备操作函数集的指针

struct request_queue *

queue

指向磁盘请求队列的指针

void *

private_data

块设备驱动程序的私有数据

int

flags

描述磁盘类型的标志

   块设备gendisk fops函数指针集

类型

方法

参数

触发事件

int

(*open)

struct block_device*, fmode_t

打开块设备文件,增加引用计数

int

(*release)

struct gendisk*, fmode_t

关闭对块设备文件的最后一个引用,减少引用计数

int

(*ioctl)

struct block_device*, fmode_t, unsigned,unsigned long

在块设备文件上发出ioctl()系统调用

   request_queue请求队列描述符中的关键字段

类型

字段

说明

struct list_head

queue_head

待处理请求的链表

make_request_fn*

make_request_fn

设备驱动程序的请求处理函数

   request描述符的关键字段

类型

字段

说明

struct list_head

queuelist

请求队列链表的指针

struct bio*

bio

请求中第一个没有完成传送操作的bio,不能直接对该成员进行访问;而要使用rq_for_each_bio访问

struct bio*

biotail

请求链表中末尾的bio

   bio结构中的关键字段

类型

字段

说明

sector_t

bi_sector

块I/O操作的第一个磁盘扇区

struct bio*

bi_next

链接到请求队列中的下一个bio

struct block_device *

bi_bdev

指向块设备描述符的指针

unsigned long

bi_flags

bio的状态标志

unsigned long

bi_rw

I/O操作标志

unsigned short

bi_vcnt

bio的bio_vec数组中段的数目

unsigned short

bi_idx

bio的bio_vec数组中段的当前索引值

unsigned int

bi_phys_segments

合并之后bio中物理段的数目

unsigned int

bi_size

需要传送的字节数

unsigned int

bi_seg_front_size

第一个可合并的段大小

unsigned int

bi_seg_back_size

最后一个可合并的段大小

unsigned int

bi_max_vecs

bio的bio_vec数组中允许的最大段数

struct bio_vec*

bi_io_vec

指向bio的bio_vec数组中的段的指针

atomic_t

bi_cnt

bio的引用计数

bio_end_io_t*

bi_end_io

bio的I/O操作结束时调用的方法

void*

bi_private

通用块层和块设备驱动程序的I/O完成方法使用的指针

   bio_vec结构中的字段

类型

字段

说明

struct page*

bv_page

指向段的页框中页描述符的指针

unsigned int

bv_len

段的字节长度

unsigned int

bv_offset

页框中中段数据的偏移量

   核心API函数

类型

函数名

输入参数

返回值

说明

int

register_blkdev

unsigned int major, const char* name

成功返回主设备号,失败返回一个负数。

向系统申请注册一个名为“name”的主设备号,当主设备号设为0时由内核自动分配一个可用的主设备号;若自己指定时,需要确保不与已有设备冲突。

struct gendisk*

alloc_disk

int minors

成功返回一个指向gendisk描述符的指针,失败返回NULL

分配一个gendisk结构,minors为次设备号的总数,一般也就是磁盘分区的数量,为1时表示该设备不能被分区,此后minors不能被修改

struct request_queue*

blk_alloc_queue

gfp_t gfp_mask

成功返回一个指向request_queue的指针,失败时返回NULL

申请请求队列,并给队列分配空间,该队列需要用户自己去调用blk_queue_make_request函数进行初始化,其中的make_request_fn函数也需要用户自己实现

void

blk_queue_make_request

struct request_queue* q, make_request_fn* mfn

无返回

初始化一个设备的请求队列,其参数make_request_fn需要我们自己实现

void

add_disk

struct gendisk *disk

无返回

将gendisk添加到系统中。调用该函数后,会在“/dev/”下显示出块设备名字,设备名字即是disk->disk_name的内容。对add_disk()的调用必须发生在驱动程序的初始化工作完成并能响应磁盘的请求之后。

void

del_gendisk

struct gendisk* disk

无返回

将gendisk从系统中删除。gendisk是一个引用计数结构,通常对del_gendisk的调用会删除gendisk中的最终计数,但是没有机制能保证其肯定发生。因此当调用此函数后,该结构可能继续存在(而且内核可能会调用我们提供的各种方法)。

void

put_disk

struct gendisk* disk

无返回

释放驱动分配的gendisk结构

void

unregister_blkdev

unsigned int major, const char *name

无返回

注销驱动程序,释放申请的主设备号

void

blk_cleanup_queue

struct request_queue *q

无返回

释放分配的请求队列

void

bio_endio

struct bio *bio, int error

无返回

返回对bio请求的处理结果,第一个参数即为被处理的bio指针,第二个参数成功时为0,失败时为-ERRNO。

1.4继续完善170行代码至200行 - 加入bio过滤功能

刚才我们已经看到了一个真时的虚拟块设备驱动,我们看看这个块设备加载到系统中,linux内核的IO栈发生了怎样的变化。

 

看到有什么问题吗?对了,请求到我们这块就结束了,为啥,通道被堵上了,那怎么打通呢?这是我们接下来增加30行代码至200行,来把通道打开,注意仅仅是修改了make_request这个函数,并且我们增加了新的东东,请大家跟这我们进一步学习。

      

       区别在于下半部,请求达到fbd_dev1和fbd_dev2后会继续被过滤到更底层的块设备/dev/sdb和/dev/sdc上,接下来我们看看这一步是如何在新增的30行代码中做到的。

仍然是fbd_driver.c fbd_driver.h Makefile三个文件,Makefile文件内容不变,我们不再贴出,然后头文件我们先贴一下。

  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个基本头文件,主要是新增加了数据结构fbd_dev的三个成员,从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记录将来要创建的fbd_dev设备的容量大小,在第一节中我们是定义了一个常量宏即512M,现在我们既然要加入BIO过滤功能,这个容量需要保持与fbd_dev底层设备大小一致,这个容量的获取我们在后面会分析到。然后是lower_dev_name记录了底层设备的文件名字,lower_bdev则保存着底层设备的block_device描述符,这个指针的用处后面我们也会讲解。

我们把完善后的fbd_driver.c再贴一下,来分析一下如果设计make_request函数,让请求可以通过我们的过滤块设备后被提交到真正的底层设备上去,而不是直接在我们这一层返回退出。

  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相对于第一个版本的差异,大家可以比较一下,可以用我们在上册内核编译时介绍的diff 命令对比一下第一节中的fbd_driver.c和本节的fbd_driver.c有什么差异,我们把patch文件也贴一下,并继续分析一下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两个设备的描述符初始化了一下,与第一版本不同,我们针对每个成员都进行了赋值,如下:

  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,表示还没有为它们分配好数据结构,然后lower­_dev_name指定为”/dev/sdb”,表示fbd_dev1这个块设备底层的块设备是sdb设备,由此我们会想到,后续进入fbd_dev1设备的请求,我们会将其过滤转发到sdb上,在此我们通过简单静态赋值的方式实现,在后面我们的项目实战训练中,我们会带领大家实现如何完善我们的代码做到灵活的动态指定底层设备;lower_bdev指针也被赋值为NULL,后面我们就会讲到如何获取sdb设备的block_device描述符,最后size初始化为0。同样fbd_dev2也进行了一样的初始化操作。通过初始化我们将fbd_dev1和fbd_dev2两个设备各自绑定了其底层的设备,这部分也是为后续真正在IO路径上实现请求过滤转发做好了基本准备,但是大家一定会清楚,现在的准备工作其实还很简单,fbd_dev设备还没有真正与sd#设备建立联系,我们继续往下分析。

直接看92-97行,这一段代码完成了fbd_dev中lower_bdev和size成员的最终赋值操作,我们看到lower­_bdev指针通过调用open_bdev_excl函数获得,该函数是内核用于打开指定路径的块设备并返回block_device数据结构指针的函数,通过该函数我们获取到了底层/dev/sd#设备的block_device地址,最终与sd#设备真正的建立了联系,然后99行,通过调用get_capacity函数我们获取到了底层设备的容量大小,然后在101行把该容量也设置进fbd_dev设备的gendisk描述符中,至此过滤功能的准备工作彻底完成。以上也是fbd_dev设备创建过程中新增加的处理逻辑。

然而设备创建好,还需在make_request请求处理函数上重新进行了一下设计,才能真正做到在IO路径上对请求进行过滤转发,我们看48-61行的make_request函数发生了哪些改变,如下,我们再贴一下。

 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 }

关键的地方是58-59行,不像第一节的代码,我们这里的请求处理函数完成了一个重要的功能,它把传入参数bio重新修饰了,58这一行中我们把bio->bi_bdev赋值为底层设备的指针lower_bdev,而lower_bdev就是我们刚才介绍的在设备初始化中通过调用open_bdev_excl函数获得的,也就是说我们告诉请求的下一站地址去哪,联系我们举的图书馆的例子,请求就这样被一层一层传递下去了,最后59行,我们调用请求提交函数把我们修饰完的请求继续提交给底层的块设备驱动了,这个函数是submit_bio,不同于第一个版本中调用bio_endio直接掐掉请求终止结束,这里通过submit_bio继续把请求转发下去了。

       至此我们的过滤块设备驱动真正做到了转发请求,我们的驱动程序终于具备了一个正常的块设备的基本功能了,我们把bio在内核栈中的流动过程再画一张图描述一下。

 

       我们赶紧试试吧,make完成后,我们加载fbd_driver.ko模块看看,然后在/dev/下找到我们的设备文件fbd_disk,然后dd一下该设备是否可以正常进行读写操作了,至此一个基本的块设备驱动我们已经完成,接下来我们继续介绍请求转发过滤后到请求真正处理完成后的回调处理过程,进一步完整的走完请求处理全过程。

猜你喜欢

转载自blog.csdn.net/zhanghaiyang9999/article/details/81975842