linux下的块设备驱动(二)
上一章主要讲了请求队列的一系列问题。下面主要说一下请求函数。首先来说一下硬盘类块设备的请求函数。
请求函数可以在没有完成请求队列的中的所有请求的情况下就返回,也可以在一个请求都不完成的情况下就返回。
下面贴出请求函数的例程:
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下的块设备驱动(二)的更多相关文章
- linux下的块设备驱动(一)
块设备的驱动比字符设备的难,这是因为块设备的驱动和内核的联系进一步增大,但是同时块设备的访问的几个基本结构和字符还是有相似之处的. 有一句话必须记住:对于存储设备(硬盘~~带有机械的操作)而言,调整读 ...
- Linux块设备驱动(二) _MTD驱动及其用户空间编程
MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...
- linux下获得块设备大小
运行结果如下 jackie@Ubuntu:~/work/0602$ sudo ./a.out /dev/sda/dev/sda3907029168,2000398934016 //BLKGETSIZE ...
- Linux设备驱动--块设备(二)之相关结构体
上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...
- Linux设备驱动--块设备(二)之相关结构体(转)
上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...
- linux 块设备驱动 (三)块设备驱动开发
一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...
- Linux 块设备驱动 (一)
1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ...
- Linux块设备驱动(一) _驱动模型
块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ...
- 乾坤合一~Linux设备驱动之块设备驱动
1. 题外话 在蜕变成蝶的一系列学习当中,我们已经掌握了大部分Linux驱动的知识,在乾坤合一的分享当中,以综合实例为主要讲解,在一个月的蜕茧成蝶的学习探索当中,觉得数据结构,指针,链表等等占据了代码 ...
随机推荐
- Arcgis镶嵌数据集java代码操作
转自:http://www.cdtarena.com/javapx/201307/9105.html 镶嵌数据集结合了之前arcgis管理影像的栅格目录和栅格数据集,为解决海量影像管理提供了很好的方案 ...
- boost 分析命令行参数
#include <boost/program_options.hpp> #include <iostream> #include <vector> using n ...
- win7系统远程连接其它计算机,并且向远程机传输文件
首先,打开开始菜单,在程序自带的 “附件“ 中找到 "远程桌面连接"并打开,出现远程桌面对话框: 其次,在对话框左下角点击“选项”,选择“本地资源对话框”,在本地设备和资源下点击“ ...
- 【代码】Android: 怎样设置app不被系统k掉
有一种方法可以设置app永远不会被kill,AndroidManifest.xml 中添加: android:persistent="true" 适用于放在/system/app下 ...
- TCP拥塞控制算法内核实现剖析(十)
内核版本:3.2.12 主要源文件:linux-3.2.12/ net/ ipv4/ tcp_veno.c 主要内容:Veno的原理和实现 Author:zhangskd @ csdn blog 概要 ...
- Android-onInterceptTouchEvent()和onTouchEvent()总结
老实说,这两个小东东实在是太麻烦了,很不好懂,我自己那api文档都头晕,在网上找到很多资料,才知道是怎么回事,这里总结一下,记住这个原则就会很清楚了: 1.onInterceptTouchEvent( ...
- MongoDB系列之二(主动复制)
目前我正在进行MongoDB的双机热备方面相关的工作.根据我目前看到的MongoDB方面的材料,MongoDB的实际部署有三种方式,分别是“主动复制”,“副本集”以及“分片副本集”. 首先我们从最简单 ...
- 宽屏手机显示9.png的图片拉伸不均衡
制作的一个.9的背景图片,在一般的480宽的手机上显示没有问题,正常拉伸,用三星的一个宽屏手机测试时,没有完全拉伸,一边拉伸多一点,一边拉伸少一点 决绝办法:就是在制作.9的时候,我在横向拉伸的地方, ...
- Random Teams
n participants of the competition were split into m teams in some manner so that each team has at le ...
- 基于visual Studio2013解决C语言竞赛题之1039移动
题目 解决代码及点评 /* 39. 有n个整数,编程序将前面的各个数依次向后移动k个位置, 最后k个数移到最前边的k个位置(见下图,其中n=8,k=3). */ # ...