最近在研究块设备驱动的编写,看了赵磊大牛的《写一个块设备驱动》,受益匪浅,虽然能看懂里面说的,但动手写写代码还是能加深理解的,下面实现的ramdisk写的很简单,如果有错误,欢迎大牛们指正哈!

分配一块内存区存放ram disk数据

  为了简单,我直接静态分配了一个大小为16MB的内存区,当然对于编写驱动来说这个空间有点大。不过就是为了简单嘛,可以理解。

#define SIMP_BLKDEV_BYTES            (16 * 1024 * 1024)
unsigned char simp_blkdev_data[SIMP_BLKDEV_BYTES];

分配一个请求队列

  可以通过函数blk_alloc_queue分配一个默认的请求队列,用该方法生成的请求对面没有设置默认的IO调度器。如果调用blk_init_queue函数分配一个请求队列,会设置默认的IO调度器。因为是编写ram disk,不需要访问外部设备,所以不需要使用IO调度器,故使用blk_alloc_queue来分配一个请求队列。

simp_blkdev_queue = blk_alloc_queue(GFP_KERNEL);

设置自己的make_request_fn函数

  blk_alloc_queue分配的请求队列中make_request_fn是没有被赋值的,这也导致了前面说的不会使用默认的IO调度器,那么我们就必须自己实现这个函数,因为上层代码向请求队列发生请求时都是通过这个函数来完成的。因为我们使用内存来模拟块设备,所以其实连请求队列都不需要,上面分配它仅仅为了让上层代码能够使用请求队列中的make_request_fn函数,否则上层代码会不知道去哪里调用make_request_fn。

  对于上层代码发出的请求,可以直接用make_request_fn函数来完成请求并直接将结果返回给上层的代码。具体如下:

static void simp_blkdev_make_request(struct request_queue *q, struct bio *bio)
{
struct bio_vec *bvec;
int i;
void *dsk_mem;
...
dsk_mem = simp_blkdev_data + (bio->bi_sector << );
/* 遍历 bio 中所有的 bvec */
bio_for_each_segment(bvec, bio, i)
{
void *iovec_mem; switch(bio_rw(bio))
{
case READ:
case READA: /* 读和预读都进行同样的处理 */
/* 用 kmalloc 将请求页映射到非线性映射区域进行
* 访问,这种方法主要是为了兼容高端内存,
* (bvec->bv_page 可能源于高端内存) */
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));
bio_endio(bio, -EIO);
return ;
}
dsk_mem += bvec->bv_len;
}
bio_endio(bio, );
}

  上面代码之间通过判断bio中的请求类型来判断具体的操作。bio_endio是用来返回给调用者make_request_fn执行结果的。有了上面自定义的make_request_fn,我们还要把该函数的地址赋值给请求队列中的make_request_fn,这样上层代码就可以使用我们自定义的这个函数了,这个可以通过函数blk_queue_make_request来完成,该函数的作用就是为请求队列绑定make_request_fn方法。

  从上面的代码中可以看出,我们直接在make_request_fn中完成了上层代码的请求,但通常的方法make_request_fn仅仅是更加上层的请求生成request结构,并将该request插入到请求队列中,再由请求队列中的request_fn来完成请求队列中的请求。为什么要把上层的请求先插入到请求队列中,而不是像我们上面那样直接处理请求呢?原因是这些请求大多数都是涉及到慢速的磁盘操作,缓存这些请求到请求队列中有利于合并相邻的请求和排序请求(IO调度程序要做的事情),这样有利于减少磁盘寻道的时间,大家都知道磁盘的寻道时间是非常慢的。而在这里由于我们涉及的只是内存操作,所有就没有必须用这么复杂的机制了,直接像处理字符设备请求那样,来一个请求就处理一个。

分配一个gendisk

  每个块设备都对应一个gendisk实例,这里也不例外,我们必须把为内存分配一个gendisk结构来把内存模拟为一个块设备。可以直接调用函数alloc_disk来分配一个gendisk结构。现在的问题是给设备分配一个什么设备号,可以可以随便选一个呢?显然是不行的,由于很多的设备号linux已经欲分配给了特定的设备,如果恰好选择了当前系统正在使用的设备号作为我们的设备号,那很显然最后会找不到我们的驱动程序,所以我们必须选一个系统一般都不用的设备号,打开linux/include/linux/major.h文件,我们会发现COMPAQ_SMART2_MAJOR到COMPAQ_SMART2_MAJOR7有8个之多的设备号,并且貌似这个设备号很少使用,所以我们就可以选着它了。有了gendisk结构,接下来就是根据我们的需要初始化这个结构了,具体如下:

/* 分配一个 gendisk 结构 */
simp_blkdev_disk = alloc_disk();
/* 填充 gendisk 主要结构成员 */
strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);
simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;
simp_blkdev_disk->first_minor = ;
simp_blkdev_disk->fops = &simp_blkdev_fops;
simp_blkdev_disk->queue = simp_blkdev_queue;
set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES >> );

  simp_blkdev_disk->disk_name设置磁盘的名字,这里我设置为cc,这个名字最后会作为设备文件名字出现在/dev目录中,方便我们最后读写这个设备文件。

  simp_blkdev_disk->major是分配给我们这个虚拟设备的设备号,我选的是COMPAQ_SMART2_MAJOR,当然也可以选择其他的。simp_blkdev_disk->first_mino表示分配给设备的第一个从设备号,一般都是从0开始。simp_blkdev_disk->fops是根具体设备相关的底层函数集,由于我们用的是内存,所以这个只有简单的使用下面定义就行:

struct block_device_operations simp_blkdev_fops =
{
.owner = THIS_MODULE,
};

simp_blkdev_disk->queue就是前面创建的请求队列,set_capacity设置我们虚拟的这个设备的大小,以扇区为单位。

  到这里一切都准备就绪了,最后只要使用add_disk函数向内核注册我们虚拟的块设备就行了。

测试

  为了运行上面写得ram disk代码,最好的办法就是使用模块了。将上面的代码封装到模块中很简单,把创建请求队列和gendisk的代码都放在模块初始化代码中,把释放请求队列和gendisk的代码放在模块退出代码中。

static void __exit simp_blkdev_exit(void)
{
del_gendisk(simp_blkdev_disk); /* 删除 gendisk 结构 */
put_disk(simp_blkdev_disk); /* 释放一个该对象的引用 */
blk_cleanup_queue(simp_blkdev_queue); /* 清理请求队列 */
}

  一切都准备就绪了,现在直接编译模块,将模块插入到内核中就可以了。在将模块插入到内核后,可以查看系统消息:

  再查看/dev目录下,我们可以看到目录下多了cc这个文件。

  好了,现在就相当于我们有了一个块设备,大小为16MB,现在可以做的事就是格式化这个设备为ext4文件系统。

  接下来我们就可以将这个文件系统挂载到某个目录上进行操作了,我们可以在这个文件系统中创建文件,目录等。

编写简单的ramdisk(无请求队列)的更多相关文章

  1. 编写简单的ramdisk(有请求队列)

    前言 前面用无请求队列实现的ramdisk的驱动程序虽然申请了请求队列,但实际上没用上,因为ramdisk不像实际的磁盘访问速度慢需要缓存,ramdisk之间使用内存空间,所以就没用请求队列了.本文将 ...

  2. 编写简单的ramdisk(选择IO调度器)

    前言 目前linux中包含anticipatory.cfq.deadline和noop这4个I/O调度器.2.6.18之前的linux默认使用anticipatory,而之后的默认使用cfq.我们在前 ...

  3. CAS简介和无锁队列的实现

    Q:CAS的实现 A:gcc提供了两个函数 bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)// ...

  4. 基于folly的AtomicIntrusiveLinkedList无锁队列进行简单封装的多生产多消费模型

    1.基于folly的AtomicIntrusiveLinkedList略微修改的无锁队列代码: #ifndef FOLLY_REVISE_H #define FOLLY_REVISE_H namesp ...

  5. Erlang运行时中的无锁队列及其在异步线程中的应用

    本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...

  6. java轻松实现无锁队列

    1.什么是无锁(Lock-Free)编程 当谈及 Lock-Free 编程时,我们常将其概念与 Mutex(互斥) 或 Lock(锁) 联系在一起,描述要在编程中尽量少使用这些锁结构,降低线程间互相阻 ...

  7. 学习 Linux,101: 自定义或编写简单脚本【转】

    转自:http://www.ibm.com/developerworks/cn/linux/l-lpic1-105-2/index.html 学习如何使用标准的 shell 语法.循环和控制结构,以及 ...

  8. zeromq源码分析笔记之无锁队列ypipe_t(3)

    在上一篇中说到了mailbox_t的底层实际上使用了管道ypipe_t来存储命令.而ypipe_t实质上是一个无锁队列,其底层使用了yqueue_t队列,ypipe_t是对yueue_t的再包装,所以 ...

  9. boost 无锁队列

    一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...

随机推荐

  1. Debian 8 jessie, OpenSSH ssh connection server responded Algorithm negotiation failed

    安装了debian 8.5 就出问题了. root@debian8:~# lsb_release -aNo LSB modules are available.Distributor ID: Debi ...

  2. SUBLIME 添加PHP控制台

    原文地址:http://www.libenfu.com/sublime-%E6%B7%BB%E5%8A%A0php%E6%8E%A7%E5%88%B6%E5%8F%B0/ 点击工具 > 编译系统 ...

  3. Editbox之三个框框

    自重装系统后,电脑中两个版本的eclipse都驾崩了,起个VS也要花费半年的时间(观赏收费),所以就运用已有的工具STS编了代码,不能用JavaFX很是遗憾,只能在网上找了代码,自己修改后完成了测试. ...

  4. mac安装虚拟机

    1. 安装VirtualBox 2. 新建,按照步骤一步步选择 3.安装系统镜像 xp_sp3_74070.iso CN_WIN7_SP1_X64_33in1_V1.2.iso 设置磁盘分区等 4.V ...

  5. linux 进程通信 管道

    1. 管道概述及相关API应用 1.1 管道相关的关键概念 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: 管道是半双工的,数据只能向一个方向流动:需要双方通信时,需要建立起两个管 ...

  6. #iOS问题记录#关于NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)

    响应Apple的号召,将APP里的HTTP请求全部升级为HTTPS,一切配置OK,正常的请求也没问题: 但,当使用SDwebImg缓存图片时,遇到了标题写的问题: 根据资料得: 这个问题的出现是因为i ...

  7. CentOS 6.5系统安装配置LAMP(Apache+PHP5+MySQL)服务器环境

    安装篇: 一.安装Apache yum install httpd #根据提示,输入Y安装即可成功安装 /etc/init.d/httpd start#启动Apache 备注:Apache启动之后会提 ...

  8. PostgreSQL配置优化

    硬件和系统配置 操作系统 Ubuntu13.04 系统位数 64 CPU Intel(R) Core(TM)2 Duo CPU 内存 4G 硬盘 Seagate ST2000DM001-1CH164 ...

  9. 分分钟用上C#中的委托和事件

    每一个初学C#的程序猿,在刚刚碰到委托和事件的概念时,估计都是望而却步,茫然摸不到头脑的.百度一搜,关于概念介绍的文章大把大把的,当然也不乏深入浅出的好文章.可看完这些文章,大多数新手,估计也只是信心 ...

  10. dagger2 备注

    dagger 2是android下的IOC框架,类似java服务端的spring,但功能上远没有其强大.个人理解不管是APP还是服务端依赖注入的本质都是一样的,用于解耦某个服务的定义和实现.我自己给出 ...