• Linux块设备和字符设备


    块设备:系统能够随机无序访问固定大小的数据片的设备,这些数据片称为块。块设备是以固定大小长度来传送资料的,它使用缓冲区暂存数据,时机成熟后从缓存中一次性写入到设备或者从设备中一次性放到缓存区。常见的块设备有硬盘、CD-ROM驱动器、Flash闪存等等,它们也是通过文件形式存在于Linux中的。Linux以“b”表示块设备。

    字符设备:按照字符流方式被有序访问,以不定长度的字元传送资料,不存在缓冲区,所以对这种设备的读写都是实时的,比如键盘、串口、印表机等等。Linux以“c”表示字符设备。

    区别:

    1.字符设备只能以字节为最小单位访问,而块设备以块为单位访问,例如512字节,1024字节等

    2.块设备可以随机访问,但是字符设备不可以

    3.字符和块没有访问量大小的限制,块也可以以字节为单位来访问

    可以看出,块设备的复杂性要远高于字符设备

     

    数据结构

    1.块设备数据结构

    struct gendisk (定义于 <linux/genhd.h>) 是单独一个磁盘驱动器的内核表示. 事实上, 内核还使用 gendisk 来表示分区

    2.字符设备数据结构

    struct file;
            struct inode;

    file定义于 <linux/fs.h>, 是设备驱动中第二个最重要的数据结构. 文件结构代表一个打开的文件. 它由内核在 open 时创建, 并传递给在文件上操作的任何函数, 直到最后的关闭. 在文件的所有实例都关闭后, 内核释放这个数据结构。

    inode 结构由内核在内部用来表示文件.inode 结构包含大量关于文件的信息其中dev_t i_rdev成员包含实际的设备编号.struct cdev *i_cdev中struct cdev 是内核的内部结构, 代表字符设备。

     

    设备访问接口

    1.块设备访问接口

    字符设备通过 file_ 操作结构使它们的操作对系统可用. 一个类似的结构用在块设备上; 它是 struct block_device_operations, 定义在 <linux/fs.h>,其主要操作方法如下:

    int (*open)(struct inode *inode, struct file *filp);
            int (*release)(struct inode *inode, struct file *filp);

    就像它们的字符驱动对等体一样工作的函数; 无论何时设备被打开和关闭都调用它们. 一个字符驱动可能通过启动设备或者锁住门(为可移出的介质)来响应一个 open 调用. 如果你将介质锁入设备, 你当然应当在 release 方法中解锁。

    int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

    实现 ioctl 系统调用的方法. 但是, 块层首先解释大量的标准请求; 因此大部分的块驱动 ioctl 方法相当短。

    2.字符设备访问接口

    struct file_operations 其中file_operation 结构中的每个成员必须指向驱动中的函数, 这些函数实现一个特别的操作, 或者对于不支持的操作留置为 NULL. 当指定为 NULL 指针时内核的确切的行为是每个函数不同的,该结构中主要函数如下:

    ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
            ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);

    filp 是文件指针, count 是请求的传输数据大小. buff 参数指向持有被写入数据的缓存, 或者放入新数据的空缓存. 最后, offp 是一个指针指向一个"long offset type"对象, 它指出用户正在存取的文件位置. 返回值是一个"signed size type"。

     

    设备注册

    1.块设备注册

    int register_blkdev(unsigned int major, const char *name);
            int unregister_blkdev(unsigned int major, const char *name);

    register_blkdev 注册一个块驱动到内核, 并且, 可选地, 获得一个主编号. 一个驱动可被注销, 使用 unregister_blkdev。

    2.字符设备注册

    int register_chrdev_region(dev_t first, unsigned int count, char *name)
            int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name)
            void unregister_chrdev_region(dev_t first, unsigned int count);

    允许驱动分配和释放设备编号的范围的函数. register_chrdev_region 应当用在事先知道需要的主编号时; 对于动态分配, 使用 alloc_chrdev_region 代替.

     

    现在我们来看看Linux系统中/dev/mtdN与/dev/mtdblockN的区别,即MTD字符设备和块设备的区别。

    /dev/mtdN是系统自身实现mtd分区锁对应的字符设备。

    MTD设备层是介于文件系统(比如jffs,因为flash不能覆写,需要先擦除再写,普通的FAT/NTFS不适用,因而设计jffs)和flash硬件驱动层之间的一个桥梁,有了mtd之后,可以为开发带来很多便利。从文件系统编写者角度看,他不需要关心使用什么类型的flash设置是其他类似的存储介质,只要调用mtd提供的接口;从硬件驱动编写者角度看,他不用关心使用了什么文件系统,只要少量mtd接口代码就能操作flash,因为mtd本身就提供了很多的驱动代码。

    mtd_part:

    struct mtd_part { 

        struct mtd_info mtd; //分区的信息 

        struct mtd_info *master; //主分区 

        uint64_t offset; //该分区的偏移地址 

        int index; //分区号 

        struct list_head list; //双向链表,链接至mtd_partition

        int registered; 

    };  

    其里面添加了一些ioctl,支持很多命令,如MEMGETINFO,MEMERASE等。

    if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
       fprintf(stderr, "%s: %s: unable to get MTD device infon", exe_name, mtd_device);
       return 1;
    }

    其中,MEMGETINFO,就是Linux MTD中的drivers/mtd/nand/mtdchar.c中的:

    static int mtd_ioctl(struct inode *inode, struct file *file,
           u_int cmd, u_long arg)
    {

    。。。。。

    case MEMGETINFO:
       info.type = mtd->type;
       info.flags = mtd->flags;
       info.size = mtd->size;
       info.erasesize = mtd->erasesize;
       info.writesize = mtd->writesize;
       info.oobsize = mtd->oobsize;
       /* The below fields are obsolete */
       info.ecctype = -1;
       info.eccsize = 0;
       if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
        return -EFAULT;
       break;

    。。。

    }

    而/dev/mtdblockN,是Nand Flash驱动中,驱动在用add_mtd_partitions()添加MTD设备分区,而生成的对应的块设备

    根据以上内容,也就更加明白,为什么不能用nandwrite,flash_eraseall,flash_erase等工具去对/dev/mtdblockN去操作了。因为/dev/mtdblock中不包含对应的ioctl,不支持你这么操作。

    2. mtd char 设备的主设备号是90,而mtd block设备的主设备号是31:

    # ls /dev/mtd? -l
    crw-r—–    1 root     root      90,   0 May 30 2007 /dev/mtd0
    crw-r—–    1 root     root      90,   2 May 30 2007 /dev/mtd1
    crw-r—–    1 root     root      90,   4 Jul 17 2009 /dev/mtd2
    crw-r—–    1 root     root      90,   6 May 30 2007 /dev/mtd3
    crwxrwxrwx    1 root     root      90,   8 May 30 2007 /dev/mtd4
    crwxrwxrwx    1 root     root      90, 10 May 30 2007 /dev/mtd5
    crwxrwxrwx    1 root     root      90, 12 May 30 2007 /dev/mtd6
    crwxrwxrwx    1 root     root      90, 14 May 30 2007 /dev/mtd7
    crwxrwxrwx    1 root     root      90, 16 May 30 2007 /dev/mtd8
    crwxrwxrwx    1 root     root      90, 18 May 30 2007 /dev/mtd9
    # ls /dev/mtdblock? -l
    brw-r—–    1 root     root      31,   0 May 30 2007 /dev/mtdblock0
    brw-r—–    1 root     root      31,   1 May 30 2007 /dev/mtdblock1
    brw-r—–    1 root     root      31,   2 May 30 2007 /dev/mtdblock2
    brw-r—–    1 root     root      31,   3 May 30 2007 /dev/mtdblock3
    brwxrwxrwx    1 root     root      31,   4 May 30 2007 /dev/mtdblock4
    brwxrwxrwx    1 root     root      31,   5 May 30 2007 /dev/mtdblock5
    brwxrwxrwx    1 root     root      31,   6 May 30 2007 /dev/mtdblock6
    brwxrwxrwx    1 root     root      31,   7 May 30 2007 /dev/mtdblock7
    brwxrwxrwx    1 root     root      31,   8 May 30 2007 /dev/mtdblock8
    brwxrwxrwx    1 root     root      31,   9 May 30 2007 /dev/mtdblock9

    此设备号,定义在/include/linux/mtd/mtd.h中 :

    #define MTD_CHAR_MAJOR   90
    #define MTD_BLOCK_MAJOR 31

    3. 其中,mtd的块设备的大小,可以通过查看分区信息获得:

    # cat /proc/partitions
    major minor #blocks name

    31     0       1024 mtdblock0
    31     1       8192 mtdblock1
    31     2     204800 mtdblock2
    31     3      65536 mtdblock3
    31     4     225280 mtdblock4

    上面中显示的块设备大小,是block的数目,每个block是1KB。

    而每个字符设备,其实就是对应着上面的每个块设备。即/dev/mtd0对应/dev/mtdblock0,其他以此类推。换句话说,mtdblockN的一些属性,也就是mtdN的属性,比如大小。

    4。对每个mtd字符设备的操作,比如利用nandwrite去对/dev/mtd0写数据,实际就是操作/dev/mtdblock0。

    而这些操作里面涉及到的偏移量offset,都指的是此mtd 分区内的偏移。比如向/dev/mtd1的offset为0的位置写入数据,实际操作的是物理偏移offset=/dev/mtd0的大小=1MB=0x100000。

    5.mtd的字符设备和块设备的命名规则,可以参考下表:

    image

    image

    参考自:

    http://blog.csdn.net/bonnshore/article/details/7860997

    http://www.crifan.com/linux_system_in__dev__mtd_and__dev__mtdblock_distinction_character_devices_and_block_devices_mtd_difference/#comments

    另外可以参考:http://blog.csdn.net/xgbing/article/details/19476979(mtd块设备缓存操作函数----mtdblock.c  讲解)

     

    Technorati Tags:
  • 相关阅读:
    Flappy Bird c++试验版
    log4net在windows 2003 iis6下使用的注意事项
    IIS6下, web.config配置为targetFramework="4.0"时出404错误
    mercurial(hg)使用
    Firebird数据库相关备忘录
    关于项目开发进度的看法
    日常数据分析的主要内容仍是结构化计算
    集算器协助java处理多样性数据源之HDFS
    集算器协助Java处理多样性数据源之Hive
    集算器协助java处理多样性数据源之JSON
  • 原文地址:https://www.cnblogs.com/simonid/p/6367693.html
Copyright © 2020-2023  润新知