zoukankan      html  css  js  c++  java
  • 识别 Linux上的设备(磁盘)类型

    原文链接:https://www.cnblogs.com/sammyliu/p/5729026.html

    1. Linux 上的设备 (device)

    Linux 操作系统中,各种设备驱动(device driver)通过设备控制器(device controller)来管理各种设备(device),其关系如下图所示:

    这些设备之中,

    • 受同一个 device driver 管理的设备都有相同的 major number,这个数字可以看作设备的类别号码,被内核用于识别一类设备
    • 受同一个 device driver 管理的同一类设备中的每一个设备都有不同的 minor number,这个数字可以看作设备编号,被设备驱动用来识别每个设备

    设备驱动主要有三大类:

    • 面向包的网络设备驱动(package oriented network device driver)
    • 面向块的存储设备驱动(block oriented storage device driver),提供缓冲式(buffered)的设备访问。
    • 面向字节的字符设备驱动 (byte oriented char device driver),有时也称为裸设备(raw devices),提供非缓冲的直接的设备访问(unbuffered direct access),比如串口设备,摄像头,声音设备等。实际上,除了网络设备和存储设备以外的其它设备都是某种字符设备。

    除此以外,还有一类设备,称为伪设备(pseudo device),它们是软件设备。Linux 上的 device 不一定要有硬件设备,比如 /dev/null, /dev/zero 等。

    关于字符设备和块设备的更多区别:

    • 块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位。大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作。
    • 块设备对于I/O 请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设备无须缓冲且被直接读写。对于存储设备而言调 读写的顺序作用巨大,因为在读写连续的扇区比分离的扇区更快。
    • 字符设备只能被顺序读写,而块设备可以随机访问。虽然块设备可随机访问,但是对于磁盘这类机械设备而言,顺序地组织块设备的访问可以提高性能。

    用户空间的各种应用是通过 device driver 来操作设备的:

    如果再详细一些就是这样的:

    图片来源

    从这个图上可以看出:

    • 网络设备驱动之上,分别有包调度器(packet scheduler),网络协议层(network protocols),NetFilter (防火墙)和 scoket 层,其中,网络设备驱动以 socket 作为应用层的接口
    • 块设备驱动之上,分别有 I/O Scheduler,通用块层(generic block layer)和文件系统,其中,块设备驱动以设备文件 (device file)作为应用层的接访问口
    • 字符设备驱动之上,分别有 Line discipline 和 terminals,其中,terminals 作为和应用的访问接口

    Linux 系统中“一切皆文件”。每个设备,在 /dev 目录中都有对一个设备文件(device file),比如 /dev/sda 表示第一个 SCSI/IDE 盘,/dev/vda 表示第一个 virtio 磁盘。应用程序通过访问这些设备文件像操作文件一样来访问这些设备,可以使用的接口包括:

    • int open(const char *path, int oflag, ... )
    • int close(int fd);
    • ssize_t write(int fd, const void *buf, size_t nbyte)
    • ssize_t read(int fd, void *buf, size_t nbyte)
    • int ioctl(int d, int request, ...)

    在 Linux 系统上,设备驱动可以被动态加载和删除

    • lsmod - 列出当前已经被加载的模块
    • insmod <module_file> - insert/load 指定的模块文件
    • modprobe <module> - insert/load 指定的 module,以及所有依赖
    • rmmod <module> - remove/unload 指定的module

    2. Linux 设备的 major 和 minor number

    2.1 用 ls 获取

    上文谈到了 major 和 number。简单地,可以从 ls 命令的输出中看出 device 的这两个numbers:

    复制代码
    root@controller:/home/sammy# cd /dev
    root@controller:/dev# ls -l
    total 0
    crw------- 1 root root     10, 175 Jul 18 15:24 agpgart
    crw------- 1 root root     10, 235 Jul 18 15:24 autofs
    brw-rw---- 1 root disk      7,   5 Jul 18 15:24 loop5
    brw-rw---- 1 root disk      7,   6 Jul 18 15:24 loop6
    brw-rw---- 1 root disk      8,   0 Jul 18 15:24 sda
    brw-rw---- 1 root disk      8,   1 Jul 18 15:24 sda1
    brw-rw---- 1 root disk      8,   2 Jul 18 15:24 sda2
    brw-rw---- 1 root disk      8,   5 Jul 18 15:24 sda5
    crw--w---- 1 root tty       4,  10 Jul 18 15:24 tty10
    crw--w---- 1 root tty       4,  11 Jul 18 15:24 tty11
    复制代码
    • 以  'c' 开头的一行表示该设备是一个字符设备,以 'b' 开头的行表示这是一个块设备。
    • 10,175 这两个数字中,前面的 10 表示 major number,后面的 175 表示 minor number。

    2.2 major 和 minor 值的设置

    历史上,设备的 major number 采用的是注册制,各设备厂家在 http://www.lanana.org/ 中注册他们的设备所使用的 major number。从 http://www.lanana.org/docs/device-list/devices-2.6+.txt 中还可以看出来 linux 2.6 内核中所分配的静态major numbers。

    但是,现在,这个注册网站已经没有人维护了,取而代之的是动态分配制度。分配者是linux 内核的 udev 模块。它将保证在本系统中,<major number>:<minor number>的组合是唯一的,而在这个范围之外,它不会保证其惟一性。一旦分配好了后,你就可以从 /proc/device 文件中读出所分配的 major numbers,比如:

    复制代码
     2 pty
     3 ttyp
     4 ttyS
     6 lp
     7 vcs
     10 misc
     13 input
     14 sound
     21 sg
    180 usbBlock devices:
     2 fd
     8 sd
     11 sr
     65 sd
     66 sd
    复制代码

    3. 根据 major 和 minior numbers 识别磁盘类型

    3.1 识别过程

    根据以下步骤来识别磁盘类型:

    (1)使用 stat 命令获取设备文件的 major 和 minor numbers。注意结果是16进制。

    复制代码
    root@u1:/dev# stat -c %T /dev/vda #minor number
    0
    root@u1:/dev# stat -c %T /dev/vdb
    10
    root@u1:/dev# stat -c %T /dev/sda
    0
    root@u1:/dev# stat -c %t /dev/vda #major number
    fd
    root@u1:/dev# stat -c %t /dev/vdb
    fd
    root@u1:/dev# stat -c %t /dev/sda
    8
    复制代码

    (2)将16进制数字转化为10进制,并拼接字符串 /sys/dev/block/$decmajor:$minor/device/driver

    /sys/dev/block/253:0/device/driver
    /sys/dev/block/253:16/device/driver
    /sys/dev/block/8:0/device/driver

    (3)调用 readlink -f 命令,获取 device driver

    复制代码
    root@u1:/dev# readlink -f /sys/dev/block/253:0/device/driver
    /sys/bus/virtio/drivers/virtio_blk

    root@u1:/dev# readlink -f /sys/dev/block/253:16/device/driver
    /sys/bus/virtio/drivers/virtio_blk
    root@u1:/dev# readlink -f /sys/dev/block/8:0/device/driver
    /sys/bus/scsi/drivers/sd
    复制代码

    从输出可以看出,/dev/vda 和 /dev/vdb 都是 virtio-block 类型的设备,而 /dev/sda 是 sd 即 SCSI 类型的设备。 

    常见的命名:

    • fd:软驱
    • hd:IDE 磁盘
    • sd:SCSI 磁盘
    • tty:terminals
    • vd:virtio 磁盘

     3.2 virtio block driver 的实现

    virtio-blk 驱动的实现代码在 https://github.com/spotify/linux/blob/master/drivers/block/virtio_blk.c。从中可以看出 major 和 minor number 分配,以及设备命名的方法。

    (1)设备命名方法

    复制代码
    if (index < 26) {
            sprintf(vblk->disk->disk_name, "vd%c", 'a' + index % 26);
        } else if (index < (26 + 1) * 26) {
            sprintf(vblk->disk->disk_name, "vd%c%c",
                'a' + index / 26 - 1, 'a' + index % 26);
        } else {
            const unsigned int m1 = (index / 26 - 1) / 26 - 1;
            const unsigned int m2 = (index / 26 - 1) % 26;
            const unsigned int m3 =  index % 26;
            sprintf(vblk->disk->disk_name, "vd%c%c%c",
                'a' + m1, 'a' + m2, 'a' + m3);
        }
    复制代码

    可见:

    • virtio-blk 设备的名称以 ‘vd’ 开头。从  ‘vda’ 开始递增,数目在 26 个以内时,增长至 ‘vdz’;如果超过 26,则从 ’vdaa‘ 一直增长至 ’vdzz‘;最高可以增长到 ’vdzzz‘。
    • 名称在设备被加载后被确定,在重新加载或者系统重启后会重新生成,因此对同一个设备其名称可能会发生变化。我的另一篇文章 理解 QEMU/KVM 和 Ceph(3):存储卷挂接和设备名称 谈到了这种变化导致的问题。

    (2)major number 是通过向内核注册来获取的

    复制代码
    static int __init init(void)
    {
        major = register_blkdev(0, "virtblk");
        if (major < 0)
            return major;
        return register_virtio_driver(&virtio_blk);
    }
    复制代码

    register_blkdev 这个方法是内核系统调用,用于注册一个块设备,需要指定主设备号。如果指定的设备号为0,则会由系统自动分配一个。该方法调用之后,就可以在/proc/devices文件中看到该块设备以及它的 major number。

    (3)minor number 是由设备的 index (索引)转化而来的

    vblk->disk->first_minor = index_to_minor(index);
  • 相关阅读:
    CodeForces 734F Anton and School
    CodeForces 733F Drivers Dissatisfaction
    CodeForces 733C Epidemic in Monstropolis
    ZOJ 3498 Javabeans
    ZOJ 3497 Mistwald
    ZOJ 3495 Lego Bricks
    CodeForces 732F Tourist Reform
    CodeForces 732E Sockets
    CodeForces 731E Funny Game
    CodeForces 731D 80-th Level Archeology
  • 原文地址:https://www.cnblogs.com/qfdy123/p/12732620.html
Copyright © 2011-2022 走看看