驱动08.块设备驱动程序

1 块设备的概述

  linux支持的两种重要的设备类型分别是字符设备和块设备,块设备可以随机地以固定大小的块传送数据。与字符设备相比,块设备有以下几个特殊之处:

  (1)块设备可以从数据的任何位置进行访问

  (2)块数据总是以固定长度进行传输,即便请求的这是一个字节

  (3)对块设备的访问有大量的缓存。当进行读时,如果已经缓存了,就直接使用缓存中的数据,而不再读设备,对于写也通过缓存来进行延迟处理。

  在块系统中,数据块指的是固定大小的数据,该固定大小由内核规定,通常是4096个字节。与数据块对应的是扇区,它是由设备硬件所决定的一个块,其大小取决于硬件,常见的硬件的扇区大多都是512个字节。数据块的大小都是扇区大小的整数倍。

2 对比之前的字符设备驱动程序的区别;

app:open  read   write

块: drv_open drv_read drv_write

       硬件

如果按照操作字符设备的那套方法来操作块设备,效率低,且容易损坏仪器。

块设备的框架:

app:      open,read,write "1.txt"
---------------------------------------------  文件的读写
文件系统: vfat, ext2, ext3, yaffs2, jffs2      (把文件的读写转换为扇区的读写)
-----------------ll_rw_block-----------------  扇区的读写
                       1. 把"读写"放入队列
                       2. 调用队列的处理函数(优化/调顺序/合并)
            块设备驱动程序     
---------------------------------------------
硬件:        硬盘,flash

3 分析ll_rw_block函数

    ll_rw_block
        for (i = 0; i < nr; i++) {
            struct buffer_head *bh = bhs[i];
            submit_bh(rw, bh);
                struct bio *bio; // 使用bh来构造bio (block input/output)
                submit_bio(rw, bio);
                    // 通用的构造请求: 使用bio来构造请求(request)
                    generic_make_request(bio);
                        __generic_make_request(bio);
                            request_queue_t *q = bdev_get_queue(bio->bi_bdev); // 找到队列  
                            
                            // 调用队列的"构造请求函数"
                            ret = q->make_request_fn(q, bio);
                                    // 默认的函数是__make_request
                                    __make_request
                                        // 先尝试合并
                                        elv_merge(q, &req, bio);
                                        
                                        // 如果合并不成,使用bio构造请求
                                        init_request_from_bio(req, bio);
                                        
                                        // 把请求放入队列
                                        add_request(q, req);
                                        
                                        // 执行队列
                                        __generic_unplug_device(q);
                                                // 调用队列的"处理函数"
                                                q->request_fn(q);

4 怎么写块设备驱动程序呢?
  1. 分配gendisk: alloc_disk
  2. 设置
  2.1 分配/设置队列: request_queue_t  // 它提供读写能力
    blk_init_queue
  2.2 设置gendisk其他信息             // 它提供属性: 比如容量
  3. 注册: add_disk
参考:drivers\block\xd.c和drivers\block\z2ram.c
--------------------------------------------------------------编辑于2017-01-13 01:03:00

 

  1 /*参考:drivers\block\z2ram.c*/
  2 
  3 
  4 #include <linux/module.h>
  5 #include <linux/errno.h>
  6 #include <linux/interrupt.h>
  7 #include <linux/mm.h>
  8 #include <linux/fs.h>
  9 #include <linux/kernel.h>
 10 #include <linux/timer.h>
 11 #include <linux/genhd.h>
 12 #include <linux/hdreg.h>
 13 #include <linux/ioport.h>
 14 #include <linux/init.h>
 15 #include <linux/wait.h>
 16 #include <linux/blkdev.h>
 17 #include <linux/blkpg.h>
 18 #include <linux/delay.h>
 19 #include <linux/io.h>
 20 
 21 #include <asm/system.h>
 22 #include <asm/uaccess.h>
 23 #include <asm/dma.h>
 24 
 25 
 26 #define RAMBLOCK_SIZE (1024*1024)
 27 #define DEVICE_NAME "ramblock"
 28 
 29 static DEFINE_SPINLOCK(ramblock_lock);
 30 
 31 static struct gendisk *ramblock_gendisk;
 32 static int major;
 33 static struct request_queue *ramblock_queue;
 34 static unsigned char *ramblock_buf;
 35 
 36 static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
 37 {
 38     geo->heads = 2;
 39     geo->cylinders = 32;
 40     geo->sectors = RAMBLOCK_SIZE/2/32/512;
 41     return 0;
 42 }
 43 
 44 
 45 static struct block_device_operations ramblock_fops = {
 46     .owner    = THIS_MODULE,
 47     .getgeo    = ramblock_getgeo,
 48 };
 49 
 50 static void do_ramblock_request(request_queue_t *q)
 51 {    
 52     struct request *req;
 53     if((req = elv_next_request(q)) != NULL)
 54     {
 55         unsigned long offset = req->sector <<9;
 56         unsigned long len = req->current_nr_sectors << 9;
 57 
 58         if (rq_data_dir(req) == READ)
 59         {
 60             memcpy(req->buffer, ramblock_buf+offset, len);
 61         }
 62         else
 63         {
 64             memcpy(ramblock_buf+offset, req->buffer, len);
 65         }
 66         end_request(req, 1);
 67             
 68     }
 69 
 70 
 71 }
 72 
 73 static int ramblock_init(void)
 74 {    
 75     ramblock_gendisk = alloc_disk(16);
 76 
 77     ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
 78        ramblock_gendisk->queue = ramblock_queue;
 79 
 80 
 81     major = register_blkdev(0, DEVICE_NAME);
 82         ramblock_gendisk->major = major;
 83         ramblock_gendisk->first_minor = 0;
 84         ramblock_gendisk->fops = &ramblock_fops;
 85         sprintf(ramblock_gendisk->disk_name, "ramblock");
 86     set_capacity(ramblock_gendisk, RAMBLOCK_SIZE / 512);
 87 
 88     ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
 89         add_disk(ramblock_gendisk);
 90 
 91 
 92     return 0;
 93 }
 94 
 95 static void ramblock_exit(void)
 96 {
 97     unregister_blkdev(major, DEVICE_NAME);
 98     del_gendisk(ramblock_gendisk);
 99     put_disk(ramblock_gendisk);
100     blk_cleanup_queue(ramblock_queue);
101     
102     kfree(ramblock_buf);
103     
104     
105 }
106 
107 module_init(ramblock_init);
108 module_exit(ramblock_exit);
109 MODULE_LICENSE("GPL");
ramblock.c

 

--------------------------------------------------------------编辑于2017-01-13 14:34:44

 

posted @ 2017-01-13 14:35  wade_linux  阅读(291)  评论(1编辑  收藏  举报