識別 Linux上的設備(磁盤)類型

1. Linux 上的設備 (device)

Linux 操做系統中,各類設備驅動(device driver)經過設備控制器(device controller)來管理各類設備(device),其關係以下圖所示:html

這些設備之中,linux

  • 受同一個 device driver 管理的設備都有相同的 major number,這個數字能夠看做設備的類別號碼,被內核用於識別一類設備
  • 受同一個 device driver 管理的同一類設備中的每個設備都有不一樣的 minor number,這個數字能夠看做設備編號,被設備驅動用來識別每一個設備

設備驅動主要有三大類:git

  • 面向包的網絡設備驅動(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 等。github

關於字符設備和塊設備的更多區別:網絡

  • 塊設備只能以塊爲單位接受輸入和返回輸出,而字符設備則以字節爲單位。大多數設備是字符設備,由於它們不須要緩衝並且不以固定塊大小進行操做。
  • 塊設備對於I/O 請求有對應的緩衝區,所以它們能夠選擇以什麼順序進行響應,字符設備無須緩衝且被直接讀寫。對於存儲設備而言調 讀寫的順序做用巨大,由於在讀寫連續的扇區比分離的扇區更快。
  • 字符設備只能被順序讀寫,而塊設備能夠隨機訪問。雖然塊設備可隨機訪問,可是對於磁盤這類機械設備而言,順序地組織塊設備的訪問能夠提升性能。

用戶空間的各類應用是經過 device driver 來操做設備的:socket

若是再詳細一些就是這樣的:svg

圖片來源oop

從這個圖上能夠看出:性能

  • 網絡設備驅動之上,分別有包調度器(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);

 

 

參考連接:

相關文章
相關標籤/搜索