写一个块设备驱动 11

转载 2012年03月30日 15:20:35

第 11章

+---------------------------------------------------+
|                 写一个块设备驱动                   |
+---------------------------------------------------+
| 作者:赵磊                                         |
| email: zhaoleidd@hotmail.com                      |
+---------------------------------------------------+
| 文章版权归原作者所有。                             |
| 大家可以自由转载这篇文章,但原版权信息必须保留。   |
| 如需用于商业用途,请务必与原作者联系,若因未取得   |
| 授权而收起的版权争议,由侵权者自行负责。           |
+---------------------------------------------------+

本章中我们仍然为块设备驱动程序使用高端内存做准备工作

这里要进行的准备工作并不意味着要增加或改变什么功能,

而是要收拾一部分代码,因为它们看起来已经有点复杂了

有编程经验的读者大概能够意识到,编程时最常做的往往不是输入程序,而是拷贝-粘贴

这是由于我们在编程时可能会不断地发现设计上的问题,或意识到还可以采用更好的结构,然后当然是

实现它

当然,更理想的情况大概是在一开始规划时就确定一个最佳的结构,以避免将来的更改,

但事实往往会与理想背道而驰,但关键是我们发现这种苗头时要及时纠正,而不是像某些部门一样去得

过且过大事化小来掩盖问题

要知道,酒是越陈越香,而垃圾却是越捂越臭,如果我们无法在最初做出完美的设计,至少我们还拥有

纠正的勇气

这里读者可能已经感觉到了,这里我们将要修改simp_blkdev_make_request()函数,因为它显得有

些大了,

以至于在前几章中对其进行修改时,不得不列出大段的代码来展示修改结果

不过这不是主要原因,相对于缩短函数长度来说,我们分割函数时可能更加在意的是提高代码的可读性

其实这里分割simp_blkdev_make_request()也是为了将来实现对高端内存的支持,

因为访问高端内存无疑将牵涉到页面映射问题,而页面映射的处理                       牵涉到了这个函数,

因此我们也希望把这部分功能独立出来,以免动戳就改动这个大函数,

也可能是为了作者的偏好,因为作者作者哪怕是改动函数中的一个字符,也会把整个函数从头到尾检查

一番,

以确定这次改动不会产生其他影响,这就解释了作者为什么更加偏爱简单一些的函数了

当然这种偏好也不一定完全是好事,比如前两天选择液晶电视时,作者就趋向于显示器+机顶盒...

----------------------- Page 81-----------------------

对于一直坚持到这一章的读者而言,应该对 simp_blkdev_make_request()函数的功能烂熟于心了,

因此我们直接列出修改后的代码:

static int simp_blkdev_trans_oneseg(struct page *start_page,
                unsigned long offset, void *buf, unsigned int len, int dir)
{
        void *dsk_mem;

        dsk_mem = page_address(start_page);
        if (!dsk_mem) {
                printk(KERN_ERR SIMP_BLKDEV_DISKNAME
                         ": get page's address failed: %p\n", start_page);
                return -ENOMEM;
        }
        dsk_mem += offset;

        if (!dir)
                memcpy(buf, dsk_mem, len);
        else
                memcpy(dsk_mem, buf, len);

        return 0;
}

static int simp_blkdev_trans(unsigned long long dsk_offset, void *buf,
                unsigned int len, int dir)
{
        unsigned int done_cnt;
        struct page *this_first_page;
        unsigned int this_off;
        unsigned int this_cnt;

        done_cnt = 0;
        while (done_cnt < len) {
                /* iterate each data segment */
                this_off = (dsk_offset + done_cnt) & ~SIMP_BLKDEV_DATASEGMASK;
                this_cnt = min(len - done_cnt,
                         (unsigned int)SIMP_BLKDEV_DATASEGSIZE - this_off);

                this_first_page = radix_tree_lookup(&simp_blkdev_data,
                         (dsk_offset + done_cnt) >> SIMP_BLKDEV_DATASEGSHIFT);
                if (!this_first_page) {

----------------------- Page 82-----------------------

                        printk(KERN_ERR SIMP_BLKDEV_DISKNAME
                                 ": search memory failed: %llu\n",
                                 (dsk_offset + done_cnt)
                                >> SIMP_BLKDEV_DATASEGSHIFT);
                        return -ENOENT;
                }

                if (IS_ERR_VALUE(simp_blkdev_trans_oneseg(this_first_page,
                        this_off, buf + done_cnt, this_cnt, dir)))
                        return -EIO;

                done_cnt += this_cnt;
        }

        return 0;
}

static int simp_blkdev_make_request(struct request_queue *q, struct bio *bio)
{
        int dir;
        unsigned long long dsk_offset;
        struct bio_vec *bvec;
        int i;
        void *iovec_mem;

        switch (bio_rw(bio)) {
        case READ:
        case READA:
                dir = 0;
                break;
        case WRITE:
                dir = 1;
                break;
        default:
                printk(KERN_ERR SIMP_BLKDEV_DISKNAME
                         ": unknown value of bio_rw: %lu\n", bio_rw(bio));
                goto bio_err;
        }

        if ((bio->bi_sector << SIMP_BLKDEV_SECTORSHIFT) + bio->bi_size
                > simp_blkdev_bytes) {
                printk(KERN_ERR SIMP_BLKDEV_DISKNAME

----------------------- Page 83-----------------------

                         ": bad request: block=%llu, count=%u\n",
                         (unsigned long long)bio->bi_sector, bio->bi_size);
                goto bio_err;
        }

        dsk_offset = bio->bi_sector << SIMP_BLKDEV_SECTORSHIFT;

        bio_for_each_segment(bvec, bio, i) {
                iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
                if (!iovec_mem) {
                        printk(KERN_ERR SIMP_BLKDEV_DISKNAME
                                 ": map iovec page failed: %p\n", bvec->bv_page);
                        goto bio_err;
                }

                if (IS_ERR_VALUE(simp_blkdev_trans(dsk_offset, iovec_mem,
                        bvec->bv_len, dir)))
                        goto bio_err;

                kunmap(bvec->bv_page);

                dsk_offset += 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;

bio_err:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
        bio_endio(bio, 0, -EIO);
#else
        bio_endio(bio, -EIO);
#endif
        return 0;
}

代码在功能上与原先没什么不同,

----------------------- Page 84-----------------------

我们只是从中抽象出处理块设备与一段连续内存之间数据传输的 simp_blkdev_trans()函数,
和同样功能的、但数据长度符合块设备数据块长度限制的 simp_blkdev_trans_oneseg()函数

这样一来,程序的结构就比较明显了:

simp_blkdev_make_request()负责决定数据传输方向、检查bio请求是否合法、遍历bio中的每个
bvec、映射bvec中的内存页,
然后把剩余的工作扔给simp_blkdev_trans() ,
而 simp_blkdev_trans()函数通过分割请求数据搞定了数据跨越多个块设备数据块的问题,并且顺便
把块设备数据块的第一个 page给找了出来,
然后邀请 simp_blkdev_trans_oneseg()函数出场
simp_blkdev_trans_oneseg()函数是幸运的,因为前期的大多数铺垫工作已经做完了,而它只要像领

导种树一样装模作样的添上最后一铲土,

就可以迎来开热烈的掌声 实际上,simp_blkdev_trans_oneseg()拿到 page指针对应的内存,然后

根据给定的数据方向执行指定长度的数据传输

simp_blkdev_trans_oneseg()不需要关心数据长度是否超出块设备数据块边界的问题,正如领导也不

会去管那棵树的死活一样

本章的代码也同样不做实验,因为我们确实也没什么好做的

至于能不能通过编译,作者已经试过了,有兴趣的读者大概可以验证一下前一句话是不是真的

作为支持高端内存的前奏,前一章和本章中做了一些可能让人觉得莫名其妙的改动

不过到此为止,准备工作已经做得差不多了,我们的程序已经为支持高端内存打下坚实的基础

下一章将进入正题,我们将实现这一期盼已久的功能

<未完,待续>


写一个块设备驱动(2)

第2章 +---------------------------------------------------+ |                 写一个块设备驱动      ...
  • newnewman80
  • newnewman80
  • 2011年11月09日 10:02
  • 724

写一个块设备驱动(11)

第11章 +---------------------------------------------------+ |                 写一个块设备驱动     ...
  • newnewman80
  • newnewman80
  • 2011年11月09日 10:08
  • 398

通过内存模拟硬盘实现一个简单的块设备驱动

本文的主要工作是通过硬盘来模拟内存,按照块设备驱动编程的框架实现一个简单的块设备驱动程序。 一、前期的准备工作 1、基本开发环境 Linux内核版本:Linux-3.4.10 开发板 ...
  • TECH_PRO
  • TECH_PRO
  • 2017年05月22日 12:54
  • 721

写一个块设备驱动程序

----------------------- Page 1----------------------- 第 1章 +----------------------------------...
  • leopard21
  • leopard21
  • 2014年03月01日 00:16
  • 1197

写一个块设备驱动

第 8章 +---------------------------------------------------+ |                 写一个块设备驱动          ...
  • bm7623
  • bm7623
  • 2012年03月30日 15:17
  • 388

RAMDISK块设备驱动程序

from: http://blog.163.com/hjw_vc/blog/static/1148310352009104952466/ ram、loop、网络设备等驱动通常使用自己编写的m...
  • damotiansheng
  • damotiansheng
  • 2015年03月31日 13:58
  • 1076

块设备驱动架构分析

1. 块设备概念:块设备是指只能以块为单位进行访问的设备,块的大小一般是512个字节的整数倍。常见的块设备包括硬件,SD卡,光盘等。 2. 块设备驱动的系统架构 2.1 系统架构---VFS VFS是...
  • coding__madman
  • coding__madman
  • 2016年06月06日 22:34
  • 3840

学写块设备驱动(三)----踢开IO调度器,自己处理bio(上)

前两篇我们编写了在内存中的最简单的块设备驱动程序,并为其更换了我们心仪的’noop‘IO调度器。本篇我们试着搞清楚内核的块设备层在这里为我们做的事情,以及我们如何做点自己想做的事情。 其实,我们前面...
  • magic_coder
  • magic_coder
  • 2012年01月09日 14:12
  • 3299

块设备驱动程序之一

一、块设备概述 linux支持的两种重要的设备类型分别是字符设备和块设备,块设备可以随机地以固定大小的块传送数据。与字符设备相比,块设备有以下几个特殊之处: 块设备可以从数据的任何位置进行访问块数据...
  • goodluckwhh
  • goodluckwhh
  • 2014年02月10日 23:41
  • 4236

一个简单的块设备驱动的实现

一个简单的块设备驱动的实现 这篇文章系列来自原创作者:赵磊,感谢这位作者,链接为:http://bbs.chinaunix.net/thread-2017377-1-1.html ...
  • chenglinhust
  • chenglinhust
  • 2013年05月09日 11:23
  • 1730
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:写一个块设备驱动 11
举报原因:
原因补充:

(最多只允许输入30个字)