Linux下块(block)设备查看指令
2022-08-17 13:40:40 阿炯

块设备(block-device)是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到缓冲区。块设备的应用在Linux中是一个完整的子系统,同时也是一个较大的且非常重要的模块单元,linux 内核使用 block_device 表示块设备:


块设备驱动整体框架

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

由通用块层(Generic Block Layer)负责维持一个I/O请求在上层文件系统与底层物理磁盘之间的关系。在通用块层中,通常用一个bio结构体来对应一个I/O请求。Linux提供了一个gendisk数据结构体,用来表示一个独立的磁盘设备或分区,用于对底层物理磁盘进行访问。在gendisk中有一个类似字符设备中file_operations的硬件操作结构指针,是block_device_operations结构体。

和字符设备驱动一样,我们需要向内核注册新的块设备、申请设备号,块设备注册函数为register_blkdev:
int register_blkdev(unsigned int major,const char *name)

major:主设备号;name:块设备名字。如果参数 major 在 1~255 之间的话表示自定义主设备号,那么返回 0 表示注册成功,如果返回负值的话表示注册失败。如果 major 为 0 的话表示由系统自动分配主设备号,那么返回值就是系统分配的主设备号(1~255),如果返回负值那就表示注册失败。

注销块设备
和字符设备驱动一样,如果不使用某个块设备了,那么就需要注销掉,函数为unregister_blkdev,函数原型如下:
void unregister_blkdev(unsigned int major,const char *name)

major:要注销的块设备主设备号;name:要注销的块设备名字。

内核使用 gendisk 来描述一个磁盘设备,这是一个结构体。在里面有几个重要的成员变量:major 为磁盘设备的主设备号,first_minor 为磁盘的第一个次设备号,minors 为磁盘的次设备号数量。block_device_operations 结构体类型,和字符设备操作集 file_operations 一样。queue 为磁盘对应的请求队列,所以针对该磁盘设备的请求都放到此队列中,驱动程序需要处理此队列中的所有请求。

编写块的设备驱动的时候需要分配并初始化一个 gendisk,linux 内核提供了一组 gendisk 操作函数来处理。


内核中相关数据结构
block_device: 描述一个分区或整个磁盘对内核的一个块设备实例
gendisk: 描述一个通用硬盘(generic hard disk)对象
hd_struct: 描述分区应有的分区信息
bio: 描述块数据传送时怎样完成填充或读取块给
driverrequest: 描述向内核请求一个列表准备做队列处理
request_queue: 描述内核申请request资源建立请求链表并填写BIO形成队列。

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

块设备中最小的可寻址单元是扇区,扇区大小一般是2的整数倍,最常见的大小是512字节。扇区的大小是设备的物理属性,扇区是所有块设备的基本单元,块设备无法对比它还小的单元进行寻址和操作,不过许多块设备能够一次就传输多个扇区。虽然大多数块设备的扇区大小都是512字节,不过其它大小的扇区也很常见,比如,很多CD-ROM盘的扇区都是2K大小。不管物理设备的真实扇区大小是多少,内核与块设备驱动交互的扇区都以512字节为单位。

类Unix操作系统都是基于文件概念的,文件是由字节序列而构成的信息载体。据此可以把IO设备当作设备文件这种所谓的特殊文件来处理;因此与磁盘上的普通文件进行交互所用的同一系统调用可直接用于IO设备。根据设备驱动程序的基本特征,设备文件可以分为两种:块和字符。这两种硬件设备之间的差异如下:
1).块设备的数据可以随机地被访问,而且从人类用户的观点看,传送任何数据块所需的时间都是较少而且是大致相同的,块设备的典型例子是硬盘。块设备一般情况下是带缓冲区的。
2).字符设备的数据或者不可以随机访问,或者可以被随机访问,但是访问随机数据所需的时间很大程度上依赖于数据在设备内的位置。

稍带讲一下字符设备(Character device)

它是一个顺序的数据流设备,对这种设备的读写是按字符进行的,而且这些字符是连续地形成一个数据流。其不具备缓冲区,所以对这种设备的读写是实时的:
扇区(Sectors):任何块设备硬件对数据处理的基本单位。通常,1个扇区的大小为512byte。(对设备而言)
块 (Blocks):由Linux制定对内核或文件系统等数据处理的基本单位。通常,1个块由1个或多个扇区组成。(对Linux操作系统而言)
段(Segments):由若干个相邻的块组成,是Linux内存管理机制中一个内存页或者内存页的一部分。

页、段、块、扇区之间的关系图如下:


块设备驱动要远比字符设备驱动复杂得多,不同类型的存储设备又对应不同的驱动子系统,块设备是针对存储设备的,比如 SD 卡、EMMC、NAND Flash、Nor Flash、SPI Flash、机械硬盘、固态硬盘等。因此块设备驱动其实就是这些存储设备驱动,块设备驱动相比字符设备驱动的主要区别如下:
1).块设备只能以块为单位进行读写访问,块是 linux 虚拟文件系统(VFS)基本的数据传输单位。
2).块设备在结构上是可以进行随机访问的,对于这些设备的读写都是按块进行的,块设备使用缓冲区来暂时存放数据,等到条件成熟以后在一次性将缓冲区中的数据写入块设备中。

其目的为了提高块设备寿命,为了提高块设备寿命而引入了缓冲区,数据先写入到缓冲区中,等满足一定条件后再一次性写入到真正的物理存储设备中,这样就减少了对块设备的擦除次数,提高了块设备寿命。字符设备是以字节为单位进行数据传输的,不需要缓冲。字符设备是顺序的数据流设备,字符设备是按照字节进行读写访问的。字符设备不需要缓冲区,对于字符设备的访问都是实时的,而且也不需要按照固定的块大小进行访问。

块设备简单分类:SCSI块设备和LVM逻辑卷块设备;
创建块设备需要两个linux内核函数:alloc_disk与add_disk;
    alloc_disk:用于分配一个gendisk结构体的实例;
    add_disk:将该结构体实例注册到系统中;
网络块设备NBD(Network Block Device):NBD本身是一个CS(Client - Sever)架构的程序;
do_nbd_request,该函数是NBD块设备的核心,其将一个块请求转换为一个网络请求,并发送给NBD服务端进行处理;
DRDB 全称为Distributed Relicated Block Device (分布式复制块设备)

在介绍完块设备的一些基础知识后,再来盘点一些相关的查看指令。

blkid
lsblk

blktrace & blkparse
blockdev



blkid

在Linux下可以使用blkid命令对查询设备上所采用文件系统类型进行查询。blkid主要用来对系统的块设备(包括交换分区)所使用的文件系统类型、LABEL、UUID等信息进行查询。要使用这个命令必须安装e2fsprogs软件包。

语法
blkid -L | -U
blkid [-c ] [-ghlLv] [-o] [-s ][-t ] -[w ] [ ...]
blkid -p [-s ] [-O ] [-S ][-o] ...
blkid -i [-s ] [-o] ...

选项
-c    # 指定cache文件(default: /etc/blkid.tab, /dev/null = none)
-d    # don't encode non-printing characters
-h    # 显示帮助信息
-g    # garbage collect the blkid cache
-o <format>    # 指定输出格式
-k    # list all known filesystems/RAIDs and exit
-s <tag>    # 显示指定信息,默认显示所有信息
-t <token>    # find device with a specific token (NAME=value pair)
-l    # look up only first device with token specified by -t
-L    # convert LABEL to device name
-U    # convert UUID to device name
-v    # 显示版本信息
-w <file>    # write cache to different file (/dev/null = no write) specify device(s) to probe (default: all devices)
Low-level probing options:
-p    # low-level superblocks probing (bypass cache)
-i    # gather information about I/O limits
-S <size>    # overwrite device size
-O <offset>    # probe at the given offset
-u <list>    # filter by "usage" (e.g. -u filesystem,raid)
-n <list>    # filter by filesystem type (e.g. -n vfat,ext3)

<td width="468"">

以列表形式查看详细信息(默认显示所有设备)

选项

含义

-L

通过UUID查找对应的分区

-s UUID

显示指定设备的LABEL(默认显示所有设备)

-s TYPE

显示所有设备名称

-o list



实例

1、列出当前系统中所有已挂载文件系统的类型(默认查找系统中所有块设备的简要信息,信息中有UUID和文件系统类型):
# blkid

2、显示指定设备 UUID:
# blkid -s UUID /dev/sda5

3、显示所有设备 UUID:
# blkid -s UUID

-U通过UUID查找对应的分区

4、显示指定设备 LABEL:
# blkid -s LABEL /dev/sda5

-L通过卷标查找对应的分区,卷标中如果有空格,需要用双引号""括起来。

5、显示所有设备 LABEL:
# blkid -s LABEL

6、显示所有设备文件系统:
# blkid -s TYPE

7、显示所有设备:
# blkid -o device

8、以列表方式查看详细信息:
# blkid -o list

lsblk

该命令用来查看block设备的信息,主要应用场景:获取wwnid,获取块设备列表,获取块设备类型(ssd、hdd),获取块设备的size等信息。数据来源:/sys/dev/block。

返回码:
0: success
1: failure
32: 找不到所有指定设备
64: 一些设备找到了,一些设备没找到

类似的命令:findmnt,blkid,ls
帮助信息:lsblk --help
用法:
lsblk [options] [<device> ...]

List information about block devices.

选项:
-a, --all            print all devices
-b, --bytes          print SIZE in bytes rather than in human readable format
-d, --nodeps         don't print slaves or holders
-D, --discard        print discard capabilities
-e, --exclude <list> exclude devices by major number (default: RAM disks)
-f, --fs             output info about filesystems
-i, --ascii          use ascii characters only
-I, --include <list> show only devices with specified major numbers
-J, --json           use JSON output format
-l, --list           use list format output
-m, --perms          output info about permissions
-n, --noheadings     don't print headings
-o, --output <list>  output columns
-O, --output-all     output all columns
-p, --paths          print complete device path
-P, --pairs          use key="value" output format
-r, --raw            use raw output format
-s, --inverse        inverse dependencies
-S, --scsi           output info about SCSI devices
-t, --topology       output info about topology
-x, --sort <column>  sort output by <column>

-h, --help     display this help and exit
-V, --version  output version information and exit

Available columns (for --output):
NAME  device name
KNAME  internal kernel device name
MAJ:MIN  major:minor device number
FSTYPE  filesystem type
MOUNTPOINT  where the device is mounted
LABEL  filesystem LABEL
UUID  filesystem UUID
PARTTYPE  partition type UUID
PARTLABEL  partition LABEL
PARTUUID  partition UUID
PARTFLAGS  partition flags
RA  read-ahead of the device
RO  read-only device
RM  removable device
HOTPLUG  removable or hotplug device (usb, pcmcia, ...)
MODEL  device identifier
SERIAL  disk serial number
SIZE  size of the device
STATE  state of the device
OWNER  user name
GROUP  group name
MODE  device node permissions
ALIGNMENT  alignment offset
MIN-IO  minimum I/O size
OPT-IO  optimal I/O size
PHY-SEC  physical sector size
LOG-SEC  logical sector size
ROTA  rotational device, 0是不可旋转(ssd),1是可旋转(hdd)
SCHED  I/O scheduler name
RQ-SIZE  request queue size
TYPE  device type
DISC-ALN  discard alignment offset
DISC-GRAN  discard granularity
DISC-MAX  discard max bytes
DISC-ZERO  discard zeroes data
WSAME  write same max bytes
WWN  unique storage identifier
RAND  adds randomness
PKNAME  internal parent kernel device name
HCTL  Host:Channel:Target:Lun for SCSI
TRAN  device transport type
SUBSYSTEMS  de-duplicated chain of subsystems
REV  device revision
VENDOR  device vendor

lsblk 命令输出 default 参数是-a, --all

lsblk -b,--bytes,输出单位为字节

lsblk -D,--discard, 打印遗弃功能列表, (没详细查)

lsblk -d [/dev/sdx], --nodeps,打印指定设备的信息,或者打印所有设备的信息

lsblk -e, --exclude list,排除major id列表
lsblk -e 7

lsblk -f, --fs,输出设备的文件系统信息,同lsblk -o NAME,FSTYPE,LABEL,UUID,MOUNTPOINT

lsblk -I,--include list,打印major id列表
lsblk -I 7,8

lsblk -i,--ascii,将输出信息的字符用ascii码打印

lsblk -J,--json 用json格式输出

lsblk -l, --list,打印一个表格格式的输出

lsblk -m, --perms,输出设备的权限,属主属组信息

同lsblk -o NAME,SIZE,OWNER,GROUP,MODE

lsblk -n, --noheadings,不打印标题

lsblk -o,--output list,打印指定字段细腻系,或者lsblk -o +UUID,表示在默认输出的后边加一列字段名为UUID的信息

lsblk -O, --output-all,输出所有列的信息

lsblk -P, --pairs,打印成html需要的unsafe格式字符

lsblk -p, --paths,打印设备的全部路径

lsblk -r, --raw,裸格式输出

lsblk -S, --scsi 输出scsi设备(物理设备)的信息

lsblk -s, --inverse,打印分区的依赖设备反序列输出,分区指向设备

lsblk -t, --topology,打印块设备的拓扑结构,同 lsblk -o NAME,ALIGN-MENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,ROTA,SCHED,RQ-SIZE,RA,WSAME

lsblk -V, --version 查看版本号

lsblk -x, --sort colum, 指定字段对输出信息排序
lsblk -S -x NAME

应用场景:
确认磁盘是否为ssd
lsblk -d -o +rota
lsblk -o +rota,UUID


blktrace & blkparse

blktrace 是一个针对 Linux 内核中块设备 IO 层的跟踪工具,用来收集磁盘IO 信息中当 IO 进行到块设备层(block层,所以叫blk trace)时的详细信息(如 IO 请求提交、入队、合并、完成等等一些列的信息),blktrace 需要借助内核经由 debugfs 文件系统(debugfs文件系统在内存中)来输出信息,所以用 blktrace 工具之前需要先挂载 debugfs 文件系统,blktrace需要结合 blkparse 来使用,由 blkparse 来解析 blktrace 产生的特定格式的二进制数据。

blktrace语法:
blktrace -d dev [ -r debugfs_path ] [ -o output ] [-k ] [ -w time ] [ -a action ] [ -A action_mask ] [ -v ]

blktrace选项:
 -A hex-mask        #设置过滤信息mask成十六进制mask
 -a mask            #添加mask到当前的过滤器
 -b size            #指定缓存大小for提取的结果,默认为512KB
 -d dev            #添加一个设备追踪
 -I file            #Adds the devices found in file as devices to trace
 -k                #杀掉正在运行的追踪
 -n num-sub        #指定缓冲池大小,默认为4个子缓冲区
 -o file            #指定输出文件的名字
 -r rel-path        #指定的debugfs挂载点
 -V                #版本号
 -w seconds        #设置运行的时间

文件输出
blktrace –d /dev/sda –o trace

另存结果
blkparse -i trace -o /root/trace.txt


blockdev

blockdev指令可以获取linux下块设备的属性值,以及设置一些块设备的属性值。

命令参数
block -V 输出程序的版本号
blockdev --report [devices] 列举出devices设备信息。如果没有devices,则输出所有操作系统发现的块设备。

blockdev [-v|-q] commands devices
-v 命令设置后,后面的命令行参数执行,会输出相应的提示信息。
-q 命令设置后,后面的命令行参数执行,不会输出相应的提示信息,默认也是不会输出。

可用的命令
--getsz        获取512字节的扇区的个数
--setro        设置只读
--setrw        设置读写
--getro        获得只读
--getdiscardzeroes        get discard zeroes support status
--getss        获得逻辑块(扇区)大小
--getpbsz        获得物理块(扇区)大小
--getiomin        获得最小 I/O 大小
--getioopt        获得最优 I/O 大小
--getalignoff        获得字节中的对齐偏移量
--getmaxsect        获得每次请求的最大扇区数
--getbsz        获得块大小
--setbsz <bytes>        设置块大小
--getsize        获得32位扇区个数(废弃, 使用 --getsz)
--getsize64    获得字节大小
--setra <sectors>    设置 readahead
--getra        获取 readahead
--setfra <sectors>    设置 文件系统 readahead
--getfra        获取 文件系统 readahead
--flushbufs        刷新缓存
--rereadpt        重新读取分区表


Linux下可以通过操作文件的方式来操作块设备,打开块设备后可以通过ioctl系统调用获取块设备的信息:
int ioctl(int fd,int request,unsigned long arg);

参数:
fd        文件描述符
request        传入的命令
arg        传入的参数,类型可变,长整形或者指针

以下是命令参数对应的实现:
左边是传入的命令,右边是传入的参数

可用的命令

--getsz        获取512字节的扇区的个数
BLKGETSIZE64        unsigned long long *

--setro        设置只读
BLKROSET        int *

--setrw        设置读写
BLKROSET        int *

--getro        获得只读
BLKROGET        int *

--getdiscardzeroes        get discard zeroes support status
BLKDISCARDZEROES        unsigned int *

--getss        获得逻辑块(扇区)大小
BLKSSZGET        int *

--getpbsz        获得物理块(扇区)大小
BLKPBSZGET        unsigned int *

--getiomin        获得最小 I/O 大小
BLKIOMIN        unsigned int *

--getioopt        获得最优 I/O 大小
BLKIOOPT        unsigned int *

--getalignoff        获得字节中的对齐偏移量
BLKALIGNOFF        int *

--getmaxsect        获得每次请求的最大扇区数
BLKSECTGET        unsigned short *

--getbsz        获得块大小
BLKBSZGET    int *

--setbsz <bytes>        设置块大小
BLKBSZSET        int *

--getsize        获得32位扇区个数(废弃, 使用 --getsz)
BLKGETSIZE        unsigned long *

--getsize64        获得字节大小
BLKGETSIZE64        unsigned long long *

--setra <sectors>        设置 readahead
BLKRASET            int

--getra        获取 readahead
BLKRAGET        long *

--setfra <sectors>        设置 文件系统 readahead
BLKFRASET           int

--getfra        获取 文件系统 readahead
BLKFRAGET        long *

--flushbufs        刷新缓存
BLKFLSBUF        none

--rereadpt        重新读取分区表
BLKRRPART        none

打印出所有操作系统已经获取的块设备可以查看/proc/partitions文件



该文章最后由 阿炯 于 2022-09-06 14:32:04 更新,目前是第 2 版。