switch (rq_data_dir(req)) { case READ: memcpy(req->buffer, simp_blkdev_data + (req->sector << 9), req->current_nr_sectors << 9); end_request(req, 1); break; case WRITE: memcpy(simp_blkdev_data + (req->sector << 9), req->buffer, req->current_nr_sectors << 9); end_request(req, 1); break; default: /* No default because rq_data_dir(req) is 1 bit */ break; } } } 函数使用elv_next_request()遍历struct request_queue *q中使用struct request *req表示的每一段,首先判断这个请求是否超过了我们的块设备的最大容量, 然后根据请求的方向rq_data_dir(req)进行相应的请求处理。由于我们使用的是指简单的数组,因此请求处理仅仅是2条memcpy。 memcpy中也牵涉到了扇区号到线性地址的转换操作,我想对坚持到这里的读者来说,这个操作应该不需要进一步解释了。
编码到此结束,然后我们试试这个程序: 首先编译: # make make -C /lib/modules/2.6.18-53.el5/build SUBDIRS=/root/test/simp_blkdev/simp_blkdev_step1 modules make[1]: Entering directory `/usr/src/kernels/2.6.18-53.el5-i686‘ CC [M] /root/test/simp_blkdev/simp_blkdev_step1/simp_blkdev.o Building modules, stage 2. MODPOST CC /root/test/simp_blkdev/simp_blkdev_step1/simp_blkdev.mod.o LD [M] /root/test/simp_blkdev/simp_blkdev_step1/simp_blkdev.ko make[1]: Leaving directory `/usr/src/kernels/2.6.18-53.el5-i686‘ # 加载模块 # insmod simp_blkdev.ko # 用lsmod看看。 这里我们注意到,该模块的Used by为0,因为它既没有被其他模块使用,也没有被mount。 # lsmod Module Size Used by simp_blkdev 16784008 0 ... # 如果当前系统支持udev,在调用add_disk()函数时即插即用机制会自动为我们在/dev/目录下建立设备文件。 设备文件的名称为我们在gendisk.disk_name中设置的simp_blkdev,主、从设备号也是我们在程序中设定的72和0。 如果当前系统不支持udev,那么很不幸,你需要自己用mknod /dev/simp_blkdev b 72 0来创建设备文件了。 # ls -l /dev/simp_blkdev brw-r----- 1 root disk 72, 0 11-10 18:13 /dev/simp_blkdev # 在块设备中创建文件系统,这里我们创建常用的ext3。 当然,作为通用的块设备,创建其他类型的文件系统也没问题。 # mkfs.ext3 /dev/simp_blkdev mke2fs 1.39 (29-May-2006) Filesystem label= OS type: Linux Block size=1024 (log=0) Fragment size=1024 (log=0) 4096 inodes, 16384 blocks 819 blocks (5.00%) reserved for the super user First data block=1 Maximum filesystem blocks=16777216 2 block groups 8192 blocks per group, 8192 fragments per group 2048 inodes per group Superblock backups stored on blocks: 8193
但问题是在blk_init_queue()函数中系统已经帮我们申请一个了,因此这里我们需要费点周折,把老的那个送回去。 所以我们的代码应该是: simp_blkdev_init()函数开头处: elevator_t *old_e; blk_init_queue()函数之后: old_e = simp_blkdev_queue->elevator; if (IS_ERR_VALUE(elevator_init(simp_blkdev_queue, "noop"))) printk(KERN_WARNING "Switch elevator failed, using default\n"); else elevator_exit(old_e);
为方便阅读并提高本文在google磁盘中的占用率,我们给出修改后的整个simp_blkdev_init()函数: static int __init simp_blkdev_init(void) { int ret; elevator_t *old_e;
simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL); if (!simp_blkdev_queue) { ret = -ENOMEM; goto err_init_queue; }
old_e = simp_blkdev_queue->elevator; if (IS_ERR_VALUE(elevator_init(simp_blkdev_queue, "noop"))) printk(KERN_WARNING "Switch elevator failed, using default\n"); else elevator_exit(old_e);
simp_blkdev_disk = alloc_disk(1); if (!simp_blkdev_disk) { ret = -ENOMEM; goto err_alloc_disk; }