• 块设备驱动——ramblock【转】


    转自:https://www.cnblogs.com/linux-37ge/p/10207385.html

    一. 什么是块设备、

        1.1. 一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到缓冲区。可以随机访问,块设备的访问位置必须能够在介质的不同区间前后移动

        1.2. 块设备与字符设备差异

            1.2.1. 块和字符是两种不同的访问设备的策略

            1.2.2. 同一个设备可以同时支持块和字符两种访问策略

            1.2.3. 块设备本身驱动层支持缓冲区,而字符设备驱动层没有缓冲

            1.2.4. 块设备驱动最适合存储设备

        1.3. 与块设备相关的几个单位    

            1.3.1. 扇区(Sectors):概念来自于早期磁盘,在硬盘、DVD中还有用,在Nand/SD中已经没意义了,但为了设备的统一,扇区是任何块设备硬件对数据处理的基本单位。通常,1个扇区的大小为512byte或512的整数倍。(对设备而言)

            1.3.2. 块 (Blocks):由Linux制定对内核或文件系统等数据处理的基本单位。通常,1个块由1个或多个扇区组成。(对Linux操作系统而言)

            1.3.3. 段(Segments):由若干个相邻的块组成。是Linux内存管理机制中一个内存页或者内存页的一部分。

            1.3.4. 页(Page),概念来自于内核,是内核内存映射管理的基本单位。linux内核的页式内存映射名称来源于此。

    二. 块设备的驱动框图

        2.1. 框图从宏观分为三层

            2.1.1. 虚拟文件系统层(VFS)

                a. VFS是linux系统内核的软件层,由内核开发者已经编写完成。

                b. VFS是文件系统和Linux 内核的接口,VFS以统一数据结构管理各种逻辑文件系统,接受用户层对文件系统的各种操作

                c. 向上,对应用层提供一个标准的文件操作接口;

                d. 对下,对文件系统提供一个标准的接口,以便其他操作系统的文件系统可以方便的移植到Linux上;

            2.1.2. 内核空间层

                2.1.2. 内核空间层又细分为三层

                    a. 通用块层(generic Block Layer)

                        负责维持一个I/O请求在上层文件系统与底层物理磁盘之间的关系。在通用块层中,通常用一个bio结构体来对应一个I/O请求

                    b. IO调度层

                        当多个请求提交给块设备时,执行效率依赖于请求的顺序。如果所有的请求是同一个方向(如:写数据),执行效率是最大的。内核在调用块设备驱动程序例程处理请求之前,先收集I/O请求并将请求排序,然后,将连续扇区操作的多个请求进行合并以提高执行效率(内核算法会自己做,不用你管),对I/O请求排序的算法称为电梯算法(elevator algorithm)。电梯算法在I/O调度层完成。内核提供了不同类型的电梯算法,电梯算法有

      1 noop(实现简单的FIFO,基本的直接合并与排序),

      2 anticipatory(延迟I/O请求,进行临界区的优化排序),

      3 Deadline(针对anticipatory缺点进行改善,降低延迟时间),

      4 Cfq(均匀分配I/O带宽,公平机制)

      PS:其实IO调度层(包括请求合并排序算法)是不需要用户管的,内核已经做好。

      映射层(Mapping Layer):起映射作用,将文件访问映射为设备的访问。

      VFS:对各种文件系统进行统一封装,为用户程序访问文件提供统一的接口,包含ext2,FAT,NFS,设备文件。

      磁盘缓存(Caches):将访问频率很高的文件放入其中。

      相关数据结构

      block_device:      描述一个分区或整个磁盘对内核的一个块设备实例

      gendisk:               描述一个通用硬盘(generic hard disk)对象。

      hd_struct:             描述分区应有的分区信息

      bio:                        描述块数据传送时怎样完成填充或读取块给driver

      request:                描述向内核请求一个列表准备做队列处理。

      request_queue:  描述内核申请request资源建立请求链表并填写BIO形成队列。

                c. 块设备驱动

                    块设备驱动:在Linux中,驱动对块设备的输入或输出(I/O)操作,都会向块设备发出一个请求,在驱动中用request结构体描述。但对于一些磁盘设备而言请求的速度很慢,这时候内核就提供一种队列的机制把这些I/O请求添加到队列中(即:请求队列),在驱动中用request_queue结构体描述。在向块设备提交这些请求前内核会先执行请求的合并和排序预操作,以提高访问的效率,然后再由内核中的I/O调度程序子系统来负责提交  I/O 请求,调度程序将磁盘资源分配给系统中所有挂起的块 I/O  请求,其工作是管理块设备的请求队列,决定队列中的请求的排列顺序以及什么时候派发请求到设备。

    三. 实例分析

        3.1. 实例中用ram虚拟出一个块设备,并非真正的块设备

        3.2. do_my_ramblock_request函数用于响应request

    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/errno.h>
    #include <linux/interrupt.h>
    #include <linux/mm.h>
    #include <linux/fs.h>
    #include <linux/kernel.h>
    #include <linux/timer.h>
    #include <linux/genhd.h>
    #include <linux/hdreg.h>
    #include <linux/ioport.h>
    #include <linux/init.h>
    #include <linux/wait.h>
    #include <linux/blkdev.h>
    #include <linux/blkpg.h>
    #include <linux/delay.h>
    #include <linux/io.h>
    #include <asm/system.h>
    #include <asm/uaccess.h>
    #include <asm/dma.h>


    #define RAMBLOCK_SIZE (1024*1024) // 1MB,2048扇区

    static struct gendisk *my_ramblock_disk; // 磁盘设备的结构体
    static struct request_queue *my_ramblock_queue; // 等待队列
    static DEFINE_SPINLOCK(my_ramblock_lock);
    static int major;
    static unsigned char *my_ramblock_buf; // 虚拟块设备的内存指针


    static void do_my_ramblock_request(struct request_queue *q)
    {

    struct request *req;
    static int r_cnt = 0; //实验用,打印出驱动读与写的调度方法
    static int w_cnt = 0;

    req = blk_fetch_request(q);

    while (NULL != req)
    {
    unsigned long start = blk_rq_pos(req) *512;
    unsigned long len = blk_rq_cur_bytes(req);

    if(rq_data_dir(req) == READ)
    {
    // 读请求
    memcpy(req->buffer, my_ramblock_buf + start, len); //读操作,
    printk("do_my_ramblock-request read %d times ", r_cnt++);
    }
    else
    {
    // 写请求
    memcpy( my_ramblock_buf+start, req->buffer, len); //写操作
    printk("do_my_ramblock request write %d times ", w_cnt++);
    }

    if(!__blk_end_request_cur(req, 0))
    {
    req = blk_fetch_request(q);
    }
    }
    }


    static int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)
    {
    return -ENOTTY;
    }

    static int blk_open (struct block_device *dev , fmode_t no)
    {
    printk("11111blk mount succeed ");
    return 0;
    }
    static int blk_release(struct gendisk *gd , fmode_t no)
    {
    printk("11111blk umount succeed ");
    return 0;
    }

    static const struct block_device_operations my_ramblock_fops =
    {
    .owner = THIS_MODULE,
    .open = blk_open,
    .release = blk_release,
    .ioctl = blk_ioctl,
    };

    static int my_ramblock_init(void)
    {
    major = register_blkdev(0, "my_ramblock");
    if (major < 0)
    {
    printk("fail to regiser my_ramblock ");
    return -EBUSY;
    }

    // 实例化
    my_ramblock_disk = alloc_disk(1); //次设备个数 ,分区个数 +1

    //分配设置请求队列,提供读写能力
    my_ramblock_queue = blk_init_queue(do_my_ramblock_request, &my_ramblock_lock);
    //设置硬盘属性
    my_ramblock_disk->major = major;
    my_ramblock_disk->first_minor = 0;
    my_ramblock_disk->fops = &my_ramblock_fops;
    sprintf(my_ramblock_disk->disk_name, "my_ramblcok"); // /dev/name
    my_ramblock_disk->queue = my_ramblock_queue;
    set_capacity(my_ramblock_disk, RAMBLOCK_SIZE / 512);
    /* 硬件相关操作 */
    my_ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
    add_disk(my_ramblock_disk); // 向驱动框架注册一个disk或者一个partation的接口

    return 0;
    }


    static void my_ramblock_exit(void)
    {
    unregister_blkdev(major, "my_ramblock");
    del_gendisk(my_ramblock_disk);
    put_disk(my_ramblock_disk);
    blk_cleanup_queue(my_ramblock_queue);
    kfree(my_ramblock_buf);
    }

    module_init(my_ramblock_init);
    module_exit(my_ramblock_exit);

    MODULE_LICENSE("GPL");


    复制代码
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/errno.h>
    #include <linux/interrupt.h>
    #include <linux/mm.h>
    #include <linux/fs.h>
    #include <linux/kernel.h>
    #include <linux/timer.h>
    #include <linux/genhd.h>
    #include <linux/hdreg.h>
    #include <linux/ioport.h>
    #include <linux/init.h>
    #include <linux/wait.h>
    #include <linux/blkdev.h>
    #include <linux/blkpg.h>
    #include <linux/delay.h>
    #include <linux/io.h>
    #include <asm/system.h>
    #include <asm/uaccess.h>
    #include <asm/dma.h>
    
    
    #define RAMBLOCK_SIZE (1024*1024)               // 1MB,2048扇区
    
    static struct gendisk *my_ramblock_disk;        // 磁盘设备的结构体
    static struct request_queue *my_ramblock_queue;    // 等待队列
    static DEFINE_SPINLOCK(my_ramblock_lock);
    static int major;
    static unsigned char *my_ramblock_buf;            // 虚拟块设备的内存指针
    
    
    static void do_my_ramblock_request(struct request_queue *q)
    {
    
        struct request *req;
        static int r_cnt = 0;             //实验用,打印出驱动读与写的调度方法
        static int w_cnt = 0;
        
        req = blk_fetch_request(q);
        
        while (NULL != req)
        {
            unsigned long start = blk_rq_pos(req) *512;
            unsigned long len = blk_rq_cur_bytes(req);
            
            if(rq_data_dir(req) == READ)
            {
                // 读请求
                memcpy(req->buffer, my_ramblock_buf + start, len);     //读操作,
                printk("do_my_ramblock-request read %d times
    ", r_cnt++);
            }
            else
            {
                // 写请求
                memcpy( my_ramblock_buf+start, req->buffer, len);     //写操作
                printk("do_my_ramblock request write %d times
    ", w_cnt++);
            }
    
            if(!__blk_end_request_cur(req, 0)) 
            {
                req = blk_fetch_request(q);
            }
        }
    }
    
    
    static int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)
    {
        return -ENOTTY;
    }
    
    static int blk_open (struct block_device *dev , fmode_t no)
    {
        printk("11111blk mount succeed
    ");
        return 0;
    }
    static int blk_release(struct gendisk *gd , fmode_t no)
    {
        printk("11111blk umount succeed
    ");
        return 0;
    }
    
    static const struct block_device_operations my_ramblock_fops =
    {
        .owner         = THIS_MODULE,
        .open         = blk_open,
        .release     = blk_release,
        .ioctl         = blk_ioctl,
    };
    
    static int my_ramblock_init(void)
    {
        major = register_blkdev(0, "my_ramblock");
        if (major < 0)
        {
            printk("fail to regiser my_ramblock
    ");
            return -EBUSY;
        }
        
        // 实例化
        my_ramblock_disk = alloc_disk(1);        //次设备个数 ,分区个数 +1
        
        //分配设置请求队列,提供读写能力
        my_ramblock_queue = blk_init_queue(do_my_ramblock_request, &my_ramblock_lock);
        //设置硬盘属性 
        my_ramblock_disk->major = major;
        my_ramblock_disk->first_minor = 0;
        my_ramblock_disk->fops = &my_ramblock_fops;
        sprintf(my_ramblock_disk->disk_name, "my_ramblcok");        // /dev/name
        my_ramblock_disk->queue = my_ramblock_queue;
        set_capacity(my_ramblock_disk, RAMBLOCK_SIZE / 512);
        /* 硬件相关操作 */
        my_ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
        add_disk(my_ramblock_disk);                // 向驱动框架注册一个disk或者一个partation的接口
        
        return 0;
    }
    
    
    static void my_ramblock_exit(void)
    {
        unregister_blkdev(major, "my_ramblock");
        del_gendisk(my_ramblock_disk);
        put_disk(my_ramblock_disk);
        blk_cleanup_queue(my_ramblock_queue);
        kfree(my_ramblock_buf); 
    }
    
    module_init(my_ramblock_init);
    module_exit(my_ramblock_exit);
    
    MODULE_LICENSE("GPL");
    复制代码

    五. 实例实验

        5.1. 模块安装(insmod)

    [root@musk210 driver_test]# lsmod
    Module Size Used by Not tainted
    [root@musk210 driver_test]# insmod blkdev.ko
    [root@musk210 driver_test]# lsmod
    Module Size Used by Not tainted
    blkdev 1955 0
    [root@musk210 driver_test]#


    复制代码
    [root@musk210 driver_test]# lsmod
    Module                  Size  Used by    Not tainted
    [root@musk210 driver_test]# insmod blkdev.ko 
    [root@musk210 driver_test]# lsmod
    Module                  Size  Used by    Not tainted
    blkdev                  1955  0 
    [root@musk210 driver_test]#
    复制代码

        5.2. 查看信息

            5.2.1. cat /proc/devices  

    [root@musk210 driver_test]# cat /proc/devices
    Character devices:
    1 mem
    2 pty
    3 ttyp
    4 /dev/vc/0
    4 tty
    5 /dev/tty
    5 /dev/console
    5 /dev/ptmx
    7 vcs
    10 misc
    13 input
    14 sound
    21 sg
    29 fb
    81 video4linux
    89 i2c
    90 mtd
    108 ppp
    116 alsa
    128 ptm
    136 pts
    180 usb
    189 usb_device
    204 s3c2410_serial
    251 hidraw
    252 s3c_bc
    253 pvrsrvkm
    254 rtc

    Block devices:
    1 ramdisk
    259 blkext
    7 loop
    8 sd
    31 mtdblock
    65 sd
    66 sd
    67 sd
    68 sd
    69 sd
    70 sd
    71 sd
    128 sd
    129 sd
    130 sd
    131 sd
    132 sd
    133 sd
    134 sd
    135 sd
    179 mmc
    253 my_ramblock
    254 device-mapper
    [root@musk210 driver_test]#


    复制代码
    [root@musk210 driver_test]# cat /proc/devices 
    Character devices:
      1 mem
      2 pty
      3 ttyp
      4 /dev/vc/0
      4 tty
      5 /dev/tty
      5 /dev/console
      5 /dev/ptmx
      7 vcs
     10 misc
     13 input
     14 sound
     21 sg
     29 fb
     81 video4linux
     89 i2c
     90 mtd
    108 ppp
    116 alsa
    128 ptm
    136 pts
    180 usb
    189 usb_device
    204 s3c2410_serial
    251 hidraw
    252 s3c_bc
    253 pvrsrvkm
    254 rtc
    
    Block devices:
      1 ramdisk
    259 blkext
      7 loop
      8 sd
     31 mtdblock
     65 sd
     66 sd
     67 sd
     68 sd
     69 sd
     70 sd
     71 sd
    128 sd
    129 sd
    130 sd
    131 sd
    132 sd
    133 sd
    134 sd
    135 sd
    179 mmc
    253 my_ramblock
    254 device-mapper
    [root@musk210 driver_test]#
    复制代码

             5.2.2. cat /proc/partitions

    [root@musk210 driver_test]# cat /proc/partitions
    major minor #blocks name

    179 0 3817472 mmcblk0
    179 1 264759 mmcblk0p1
    179 2 264759 mmcblk0p2
    179 3 104412 mmcblk0p3
    179 4 3158463 mmcblk0p4
    179 8 7761920 mmcblk1
    179 9 7726620 mmcblk1p1
    253 0 1024 my_r

    复制代码
    [root@musk210 driver_test]# cat /proc/partitions 
    major minor  #blocks  name
    
     179        0    3817472 mmcblk0
     179        1     264759 mmcblk0p1
     179        2     264759 mmcblk0p2
     179        3     104412 mmcblk0p3
     179        4    3158463 mmcblk0p4
     179        8    7761920 mmcblk1
     179        9    7726620 mmcblk1p1
     253        0       1024 my_r
    复制代码

              5.2.3. ls /dev/my*

    [root@musk210 driver_test]# ls /dev/my*
    /dev/my_ramblcok
    [root@musk210 driver_test]#
    [root@musk210 driver_test]# ls /dev/my*
    /dev/my_ramblcok
    [root@musk210 driver_test]#

            5.2.4. lsmod

    [root@musk210 driver_test]# lsmod
    Module                  Size  Used by    Not tainted
    blkdev                  1955  0

    [root@musk210 driver_test]# lsmod
    Module                  Size  Used by    Not tainted
    blkdev                  1955  0

        5.3. 挂载测试

            5.3.1. 挂载前要先格式化

                a. 格式化:mkfs.ext2 /dev/my_ramblock

    [root@musk210 driver_test]# mkfs.ext2 /dev/my_ramblcok
    [ 5587.367230] 11111blk mount succeed
    Filesystem label=[ 5587.372541] do_my_ramblock-request read 0 times
    [ 5587.376912] do_my_ramblock-request read 1 times
    [ 5587.381302] do_my_ramblock-request read 2 times
    [ 5587.385774] do_my_ramblock-request read 3 times
    [ 5587.390276] do_my_ramblock-request read 4 times
    [ 5587.394779] do_my_ramblock-request read 5 times
    [ 5587.399285] do_my_ramblock-request read 6 times
    [ 5587.403796] do_my_ramblock-request read 7 times
    [ 5587.408303] do_my_ramblock-request read 8 times
    [ 5587.412854] do_my_ramblock request write 0 times
    [ 5587.417321] do_my_ramblock request write 1 times
    [ 5587.421906] do_my_ramblock request write 2 times
    [ 5587.426498] do_my_ramblock request write 3 times
    [ 5587.431089] do_my_ramblock request write 4 times
    [ 5587.435684] do_my_ramblock request write 5 times
    [ 5587.440277] do_my_ramblock request write 6 times
    [ 5587.444868] do_my_ramblock request write 7 times
    [ 5587.449462] do_my_ramblock request write 8 times
    [ 5587.454133] 11111blk umount succeed

    OS type: Linux
    Block size=1024 (log=0)
    Fragment size=1024 (log=0)
    128 inodes, 1024 blocks
    51 blocks (5%) reserved for the super user
    First data block=1
    Maximum filesystem blocks=262144
    1 block groups
    8192 blocks per group, 8192 fragments per group
    128 inodes per group
    [root@musk210 driver_test]#


    复制代码
    [root@musk210 driver_test]# mkfs.ext2 /dev/my_ramblcok 
    [ 5587.367230] 11111blk mount succeed
    Filesystem label=[ 5587.372541] do_my_ramblock-request read 0 times
    [ 5587.376912] do_my_ramblock-request read 1 times
    [ 5587.381302] do_my_ramblock-request read 2 times
    [ 5587.385774] do_my_ramblock-request read 3 times
    [ 5587.390276] do_my_ramblock-request read 4 times
    [ 5587.394779] do_my_ramblock-request read 5 times
    [ 5587.399285] do_my_ramblock-request read 6 times
    [ 5587.403796] do_my_ramblock-request read 7 times
    [ 5587.408303] do_my_ramblock-request read 8 times
    [ 5587.412854] do_my_ramblock request write 0 times
    [ 5587.417321] do_my_ramblock request write 1 times
    [ 5587.421906] do_my_ramblock request write 2 times
    [ 5587.426498] do_my_ramblock request write 3 times
    [ 5587.431089] do_my_ramblock request write 4 times
    [ 5587.435684] do_my_ramblock request write 5 times
    [ 5587.440277] do_my_ramblock request write 6 times
    [ 5587.444868] do_my_ramblock request write 7 times
    [ 5587.449462] do_my_ramblock request write 8 times
    [ 5587.454133] 11111blk umount succeed
    
    OS type: Linux
    Block size=1024 (log=0)
    Fragment size=1024 (log=0)
    128 inodes, 1024 blocks
    51 blocks (5%) reserved for the super user
    First data block=1
    Maximum filesystem blocks=262144
    1 block groups
    8192 blocks per group, 8192 fragments per group
    128 inodes per group
    [root@musk210 driver_test]#
    复制代码

            5.3.2. 挂载到/tmp目录下

                a. 挂载:  mount -t ext2 /dev/my_ramblcok /tmp

    [root@musk210 driver_test]# mount -t ext2 /dev/my_ramblcok /tmp/
    [ 5695.893226] 11111blk mount succeed
    [ 5695.895247] do_my_ramblock-request read 9 times
    [ 5695.899752] do_my_ramblock-request read 10 times
    [ 5695.904346] do_my_ramblock-request read 11 times
    [ 5695.908912] do_my_ramblock request write 9 times


    复制代码
    [root@musk210 driver_test]# mount -t ext2 /dev/my_ramblcok /tmp/
    [ 5695.893226] 11111blk mount succeed
    [ 5695.895247] do_my_ramblock-request read 9 times
    [ 5695.899752] do_my_ramblock-request read 10 times
    [ 5695.904346] do_my_ramblock-request read 11 times
    [ 5695.908912] do_my_ramblock request write 9 times
    复制代码

            5.3.3. 去/tmp目录

    [root@musk210 tmp]# ls
    [ 5757.874625] do_my_ramblock-request read 12 times
    [ 5757.877941] do_my_ramblock-request read 13 times
    lost+found
    [root@musk210 tmp]# touch a.txt
    [ 5768.346032] do_my_ramblock-request read 14 times


    复制代码
    [root@musk210 tmp]# ls
    [ 5757.874625] do_my_ramblock-request read 12 times
    [ 5757.877941] do_my_ramblock-request read 13 times
    lost+found
    [root@musk210 tmp]# touch a.txt
    [ 5768.346032] do_my_ramblock-request read 14 times
    复制代码

    参考《朱老师.块设备驱动介绍》

    参阅文献:http://www.51testing.com/html/42/n-3710242.html

    【作者】张昺华
    【大饼教你学系列】https://edu.csdn.net/course/detail/10393
    【新浪微博】 张昺华--sky
    【twitter】 @sky2030_
    【微信公众号】 张昺华
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Cleaning Up Children Asynchronously
    advacing lnux program 4.1.5 Thread Attributes[copy]
    advacing lnux program Threads Return Value[copy]
    批处理,所有子文件夹下面的所有文件
    .NET连接sybase乱码问题
    Asp.net forms认证遇到的一个奇怪的问题和测试过程
    WCF错误
    wcf超时错误
    gridControl控件显示交叉表
    K3 12.1修改报表Bug
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/13897292.html
Copyright © 2020-2023  润新知