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驱动的知识,在乾坤合一的分享当中,以综合实例为主要讲解,在一个月的蜕茧成蝶的学习探索当中,觉得数据结构,指针,链表等等占据了代码 ...
随机推荐
- QThread 与 QObject的关系(QObject可以用于多线程,可以发送信号调用存在于其他线程的slot函数,但GUI类不可重入)
QThread 继承 QObject..它可以发送started和finished信号,也提供了一些slot函数. QObject.可以用于多线程,可以发送信号调用存在于其他线程的slot函数,也可以 ...
- InputStream中read()与read(byte[] b)
原文:InputStream中read()与read(byte[] b) read()与read(byte[] b)这两个方法在抽象类InputStream中前者是作为抽象方法存在的,后者不是,JDK ...
- 【C++】第二章:Hello World!
1.开发工具:Microsoft Visual C++ v6.0 2.通过IDE建立Hello World程序: 我们可以看到三个文件夹结构,分别是: Source Files(源文件). Heade ...
- 承诺消费换4G无线上网伴侣活动火热来袭,各指定营业厅即可办理
承诺消费换4G无线上网伴侣活动火热来袭,各指定营业厅即可办理 承诺消费换4G无线上网伴侣活动火热来袭,各指定营业厅即可办理
- Activity 和 Intent
Activity 和 Intent 一.Intent指向Activity 二.利用 Intent 向第二个 Activity 传数据 三.利用 Intent 接受第二个 Activity 的返回值 四 ...
- IIS Web服务扩展中添加ASP.NET4.0
问题 服务器上安装了ASP.NET 4.0.30319组件,但是在IIS的Web服务扩展中并没有找到ASP.NET v4.0.30319这项,这导致基于.NET4.0开发的网页都无法正常浏览(404错 ...
- uvc摄像头代码解析5
8.初始化uvc控制 8.1 重要结构体 struct uvc_control { //uvc控制 struct uvc_entity *entity; //uvc实体 struct uvc_cont ...
- 嵌入式ntp服务器的移植
一.交叉编译 1.官网下载http://www.ntp.org/点击download选项页 我的版本是ntp-4.2.6p5.tar.gz 2.解压 tar -zxvf ntp-4.2.6p5.tar ...
- Swift - 通过url地址打开web页面
通过UIApplication.sharedApplication().openURL()方法,可以使用浏览器打开相应的网页. 1 2 3 var urlString = "http://h ...
- Android 环境变量配置(Mac)
Mac 系统10.10,自带的就是jdk1.6,因为工作需要就升级到了1.7,要从新配置环境变量了 mac 默认是自带的有jdk1.6 安装路径为: /System/Library/Framework ...