上一章主要讲了请求队列的一系列问题。下面主要说一下请求函数。首先来说一下硬盘类块设备的请求函数。

请求函数可以在没有完成请求队列的中的所有请求的情况下就返回,也可以在一个请求都不完成的情况下就返回。
下面贴出请求函数的例程:

static int simp_blkdev_make_request(struct request_queue *q, struct bio *bio)
{
struct bio_vec *bvec;
int i;
void *dsk_mem; if ((bio->bi_sector << 9) + bio->bi_size > SIMP_BLKDEV_BYTES) {
printk(KERN_ERR SIMP_BLKDEV_DISKNAME
": bad request: block=%llu, count=%u\n",
(unsigned long long)bio->bi_sector, bio->bi_size);
//这个条件是在判断当前正在运行的内核版本。
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
bio_endio(bio, 0, -EIO);
#else
bio_endio(bio, -EIO);
#endif
return 0;
} dsk_mem = simp_blkdev_data + (bio->bi_sector << 9); //遍历
bio_for_each_segment(bvec, bio, i) {
void *iovec_mem; switch (bio_rw(bio)) {
case READ:
case READA:
iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
memcpy(iovec_mem, dsk_mem, bvec->bv_len);
kunmap(bvec->bv_page);
break;
case WRITE:
iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
memcpy(dsk_mem, iovec_mem, bvec->bv_len);
kunmap(bvec->bv_page);
break;
default:
printk(KERN_ERR SIMP_BLKDEV_DISKNAME
": unknown value of bio_rw: %lu\n",
bio_rw(bio));
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
bio_endio(bio, 0, -EIO);
#else
bio_endio(bio, -EIO);
#endif
return 0;
}
dsk_mem += bvec->bv_len;
} #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
bio_endio(bio, bio->bi_size, 0);
#else
bio_endio(bio, 0);
#endif return 0;
}

首先是一个while大循环的不断检测:

while ((req = elv_next_request(q)) != NULL) 

这个while大循环是在不断的检测,同时elv_next_request这个函数的作用是获得队列中第一个未完成的请求,其实就是在遍历队列中的请求。elv_next_request这个函数用来获得队列中第一个未完成的请求,参数是q,这里的q是请求队列request_queue的函数指针。

还有一个重要的函数就是end_request(req, 0);

在这个函数中,传给end_request这个函数的参数如果是0,则意味着请求失败。如果传的是1则意味着请求处理成功。

在这里end_request这个函数很重要,其原型如下所示:

void end_request(struct request *req, int uptodate)
{ if (!end_that_request_first(req, uptodate, req->hard_cur_sectors))
{
add_disk_randomness (req->rq_disk);
blkdev_dequeue_request (req);
end_that_request_last(req);
}
}

当设备已经完成1个IO请求的部分或者全部扇区传输后,它必须通告块设备层,上述代码中的第4行完成这个工作。
end_that_request_first()函数的原型为:

int end_that_request_first(struct request *req, int success, int count);

这个函数告知块设备层,块设备驱动已经完成count个扇区的传送。
end_that_request_first()的返回值是一个标志,指示是否这个请求中的所有扇区已经被传送。返回值为0表示所有的扇区已经被传送并且这个请求完成,之后,我们必须使用 blkdev_dequeue_request()来从队列中清除这个请求。
最后,将这个请求传递给end_that_request_last()函数:
void end_that_request_last(struct request *req);
end_that_request_last()通知所有正在等待这个请求完成的对象请求已经完成并回收这个请求结构体。
第6行的add_disk_randomness()函数的作用是使用块 I/O 请求的定时来给系统的随机数池贡献熵,它不影响块设备驱动。但是,仅当磁盘的操作时间是真正随机的时候(大部分机械设备如此),才应该调用它。

LDD讲了一个复杂的请求函数:

这个请求函数的代码如下:

static void xxx_full_request(request_queue_t *q)
{
struct request *req;
int sectors_xferred;
struct xxx_dev *dev = q->queuedata;
/* 遍历每个请求 */
while ((req = elv_next_request(q)) != NULL)
{
if (!blk_fs_request(req))
{
printk(KERN_NOTICE "Skip non-fs request\n"); end_request(req, 0);
continue;
}
sectors_xferred = xxx_xfer_request(dev, req);
if (!end_that_request_first(req, 1, sectors_xferred))
{
blkdev_dequeue_request(req);
end_that_request_last(req);
}
}
}
/* 请求处理 */
static int xxx_xfer_request(struct xxx_dev *dev, struct request *req)
{
struct bio *bio;
int nsect = 0;
/* 遍历请求中的每个bio */
rq_for_each_bio(bio, req)
{
xxx_xfer_bio(dev, bio);
nsect += bio->bi_size / KERNEL_SECTOR_SIZE;
}
return nsect;
}
/* bio处理 */
static int xxx_xfer_bio(struct xxx_dev *dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector; /* 遍历每1段 */
bio_for_each_segment(bvec, bio, i)
{
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
xxx_transfer(dev, sector, bio_cur_sectors(bio), buffer, bio_data_dir(bio)
== WRITE);
sector += bio_cur_sectors(bio);
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0;
}

复杂的请求函数结构示意图如下所示:

对于SD卡和U盘这类设备支持无请求队列的的模式。LDD说,为了使用这个模式,驱动必须提供一个制造请求函数,而不是一个请求函数。
第一个参数虽然仍然是请求队列,但是这个请求队列实际上不包含任何request,因为块层没有必要将bio调整为request所以制造请求的主要参数是bio结构体。bio_endio这个函数是通知结束处理函数。
无论处理成功与否,制造请求函数都应该返回0,如果返回一个非0数,那么bio请求将会再一次被提交。

最后制造请求函数的一个重要函数:

void bio_endio(struct bio *bio, int error)
{
if (error)
clear_bit(BIO_UPTODATE, &bio->bi_flags);
else if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
error = -EIO; if (bio->bi_end_io)
bio->bi_end_io(bio, error);
}

linux下的块设备驱动(二)的更多相关文章

  1. linux下的块设备驱动(一)

    块设备的驱动比字符设备的难,这是因为块设备的驱动和内核的联系进一步增大,但是同时块设备的访问的几个基本结构和字符还是有相似之处的. 有一句话必须记住:对于存储设备(硬盘~~带有机械的操作)而言,调整读 ...

  2. Linux块设备驱动(二) _MTD驱动及其用户空间编程

    MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...

  3. linux下获得块设备大小

    运行结果如下 jackie@Ubuntu:~/work/0602$ sudo ./a.out /dev/sda/dev/sda3907029168,2000398934016 //BLKGETSIZE ...

  4. Linux设备驱动--块设备(二)之相关结构体

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  5. Linux设备驱动--块设备(二)之相关结构体(转)

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  6. linux 块设备驱动 (三)块设备驱动开发

    一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...

  7. Linux 块设备驱动 (一)

    1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ...

  8. Linux块设备驱动(一) _驱动模型

    块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ...

  9. 乾坤合一~Linux设备驱动之块设备驱动

    1. 题外话 在蜕变成蝶的一系列学习当中,我们已经掌握了大部分Linux驱动的知识,在乾坤合一的分享当中,以综合实例为主要讲解,在一个月的蜕茧成蝶的学习探索当中,觉得数据结构,指针,链表等等占据了代码 ...

随机推荐

  1. haproxy /admin跳转 不会在接口上再次加上admin

    http://www.xx.com/admin/api/menu [root@wx03 mojo]# cat test.pl use Mojolicious::Lite; use JSON qw/en ...

  2. 香蕉派 BPI-M1+ 双核开源硬件单板计算机

    香蕉派 BPI-M1+ 开源硬件开发板 深圳市源创通信技术有限公司公司 http://www.sinovoip.com.cn/cp_view.asp?id=562 产品介绍 Banana PI BPI ...

  3. 动态链接库dll的 静态加载 与 动态加载

    dll 两种链接方式  : 动态链接和静态链接(链接亦称加载) 动态链接是指在生成可执行文件时不将所有程序用到的函数链接到一个文件,因为有许多函数在操作系统带的dll文件中,当程序运行时直接从操作系统 ...

  4. 【iOS发展-53】实例探究:scrollView使用方法和解决方案无法滚动核心

    案例效果: (1)基本的就是练习scrollView的使用方法.界面里面的其它元素基本都是UIView和UIButton堆砌起来的. (2)主要用代码实现.当然,能够先用storyboard拖个scr ...

  5. css图片上下垂直居中

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. WM_PAINT消息在窗口重绘的时候产生,那什么时候窗口会重绘(异步工作方式效率高、灵活性强,还有UpdateWindow和RedrawWindow帮忙)

    Q:wm_paint消息在窗口重绘的时候产生,那什么时候窗口会重绘?? A: 严格地说,只有当收到WM_PAINT消息后窗口会重绘 但是引起这个消息的事件有很多, 比如:1.首次创建 2.移动 3.改 ...

  7. hihoCoder 1014trie树(字典树)

    hihoCoder 1014 题目提示已经很清楚了~ 贴代码…… #include <iostream> #include <cstdio> #include <cstr ...

  8. linux 压缩和解压文件

    一.压缩:20120715文件下面所有的文件 如下: tar -zcvf 20120715.tar.gz  20120715* 二.解压20120715.tar.gz压缩包 如下: tar -xzvf ...

  9. ubuntu 常用软件

    terminator:任意分割控制台 Sublime Text:文本编辑器,也是轻量级的IDE Wireshark:抓包工具 Okular:PDF等文档编辑工具 yEd:流程图等制图软件 Shutte ...

  10. Gitolite v3安装配置指南

    使用gitolite对git仓储进行权限配置 gitolite在近期做了很多代码改动,升级到了v3版本,而我使用的是v3.5.2.在<Git权威指南>中所提及的是v2版本,有很多东西已经不 ...