Linux检测U盘大小的过程是一个涉及硬件识别、内核驱动、设备管理和用户空间工具协同工作的复杂流程,从U盘插入物理接口到用户通过命令查看大小,整个流程可划分为硬件接入、内核处理、设备注册、信息暴露和用户空间读取五个阶段,每个阶段都有明确的技术机制和交互逻辑。
硬件接入与USB子系统识别
当U盘插入Linux系统的USB接口(如USB 2.0/3.0端口)时,USB控制器(通常集成在主板芯片组中)会通过差分信号检测到设备接入的电压变化,并触发中断通知内核,USB子系统(由usbcore
内核模块管理)是处理USB设备的核心,它通过以下步骤识别设备类型:
- 设备枚举:USB控制器向设备发送标准请求,获取设备描述符(Device Descriptor),其中包含设备类型(如
0x08
表示大容量存储设备)、厂商ID(Vendor ID)和产品ID(Product ID)。 - 配置描述符解析:U盘作为大容量存储设备(Mass Storage Device),通常会响应配置描述符请求,表明其支持“USB Mass Storage Class”协议(如BOT、UAS等)。
- 接口驱动匹配:
usbcore
根据设备类型加载对应的驱动,即usb-storage
模块(或uas
模块,用于更高效的USB Attached SCSI协议),该驱动负责将USB设备的原始数据包转换为块设备(Block Device)可识别的SCSI命令或ATA命令。
块设备注册与分区表解析
usb-storage
驱动加载后,会将U盘抽象为一个块设备,块设备是Linux内核中用于存储设备的抽象层,以固定大小的块(Block,通常为512字节或4KB)为单位进行数据读写,这一阶段的核心流程包括:
- 块设备注册:
usb-storage
驱动通过blk_alloc_disk()
函数为U盘分配一个gendisk
结构(块设备的核心数据结构),并设置设备名称(如sdb
,其中s
表示SCSI设备,b
表示顺序号),随后调用add_disk()
将设备注册到块设备子系统,此时系统会自动在/dev
目录下创建设备文件(如/dev/sdb
,表示整个U盘;/dev/sdb1
表示第一个分区)。 - 读取分区表:内核通过块设备接口读取U盘的分区表,常见的分区表格式有两种:
- MBR(主引导记录):位于磁盘的第一个扇区(512字节),包含分区表项(每个项16字节,最多4个分区),每个分区项定义了分区的起始扇区、总扇区数和类型,内核解析MBR后,将分区信息记录在
struct hd_struct
中,并创建对应的分区设备文件(如/dev/sdb1
)。 - GPT(GUID分区表):位于磁盘开头和结尾(备份),每个分区条目128字节,包含分区的GUID、起始/结束扇区、类型等信息,内核通过
parted
或libparted
相关代码解析GPT,识别分区并创建设备文件。
- MBR(主引导记录):位于磁盘的第一个扇区(512字节),包含分区表项(每个项16字节,最多4个分区),每个分区项定义了分区的起始扇区、总扇区数和类型,内核解析MBR后,将分区信息记录在
- 计算设备大小:分区表解析完成后,内核会记录设备的总扇区数(通过读取设备的“容量”字段,如SCSI设备的READ CAPACITY命令),对于未分区的U盘,总扇区数即设备总大小;对于分区设备,分区大小为“结束扇区-起始扇区+1”乘以扇区大小(512B或4KB)。
sysfs文件系统:设备信息的暴露接口
为了向用户空间提供设备信息,内核通过sysfs
虚拟文件系统将块设备的元数据暴露到/sys
目录下。sysfs
是内核与用户空间交互的核心接口,U盘的大小信息主要通过以下文件存储:
/sys/block/sdb/size
:直接存储U盘的总扇区数(以512字节为单位),若U盘总容量为16GB(17179869184字节),则size
的值为17179869184 / 512 = 33554432
,用户可通过cat
命令直接读取该文件获取扇区数,再乘以512得到字节大小。/sys/block/sdb/removable
:标识设备是否为可移动设备(U盘通常为1,硬盘为0),内核通过USB设备的配置描述符判断设备是否可移除,并设置该属性。/sys/block/sdb/sdb1/size
:对于分区设备,该文件存储分区的扇区数,计算方式与设备总大小类似。
sysfs
还暴露了设备的其他信息,如队列深度(queue/nr_requests
)、物理块大小(queue/physical_block_size
)等,但大小检测主要依赖上述文件。
用户空间工具:从sysfs到命令行输出
用户通过命令行工具(如lsblk
、fdisk
、df
等)查看U盘大小时,这些工具通过读取sysfs
文件或调用系统调用(如ioctl
)获取内核提供的设备信息,以下是常用工具的原理:
命令 | 原理说明 | 输出示例(16GB U盘,分区15.9GB) | 适用场景 |
---|---|---|---|
lsblk |
遍历/sys/block 目录,读取每个块设备的size 、removable 等属性,并格式化输出。 |
sdb 16G disk /dev/sdb └─sdb1 15.9G part /dev/sdb1 |
查看设备及分层的拓扑结构 |
fdisk |
打开块设备文件(如/dev/sdb ),通过BLKGETSIZE64 ioctl获取设备总字节数,再解析分区表输出分区大小。 |
Disk /dev/sdb: 16 GiB, 17179869184 bytes |
管理分区,查看分区表详情 |
df |
需先挂载U盘,读取挂载点的文件系统超级块(如ext4的superblock ),获取文件系统总大小、已用空间等。 |
/dev/sdb1 15.9G 2.3G 13.6G 15% /media/usb |
查看已挂载文件系统的使用情况 |
blkid |
读取设备的文件系统签名(如FAT32的引导扇区、ext4的超级块),识别文件系统类型,但可通过-o size 参数显示大小。 |
/dev/sdb1: UUID="..." TYPE="vfat" SIZE="15990000*512" |
识别文件系统及元数据 |
特殊场景处理
- 未分区U盘:若U盘未分区,内核直接将其视为一个块设备(如
/dev/sdb
),lsblk
或fdisk
会显示设备的总大小,无需解析分区表。 - 4K扇区设备:现代U多采用4K物理扇区(Native 4K),但内核仍以逻辑扇区(512B)为单位管理,通过
queue/logical_block_size
和queue/physical_block_size
区分,计算大小时会自动转换(如size
文件仍以512B为单位,但实际物理容量为size * 512 / 8
)。 - 热插拔与动态更新:当U盘拔出时,
usb-storage
驱动会调用del_gendisk()
注销块设备,sysfs
中对应的文件会被移除;再次插入时,整个过程重复,实现动态检测。
相关问答FAQs
Q1:为什么df
命令显示的U盘大小和lsblk
显示的不一致?
A:df
显示的是文件系统的“可用”或“总”大小,而lsblk
显示的是设备或分区的“物理”大小,两者差异主要源于:
- 分区表开销:MBR分区表占1扇区(512B),GPT分区表占多个扇区(通常34KB),导致分区大小略小于设备总大小(如16GB U盘分区显示15.9GB)。
- 文件系统元数据:文件系统(如ext4、FAT32)会保留部分空间用于存储超级块、inode表等元数据,
df
显示的是文件系统实际可用的逻辑大小,而非物理扇区数。 - 未挂载分区:
df
需要文件系统挂载后才能读取超级块信息,若U盘未挂载,df
无法显示其大小,而lsblk
仍能通过sysfs
获取物理大小。
Q2:Linux如何区分U盘和内置硬盘?
A:Linux主要通过以下属性区分U盘和内置硬盘:
sysfs
的removable
属性:U盘插入后,内核通过USB设备的配置描述符设置/sys/block/sdb/removable
为1(可移动),而内置硬盘(如SATA硬盘)通常为0。- 设备路径:U盘的设备文件位于
/dev/bus/usb/
下(通过udev
规则管理),而内置硬盘直接位于/dev/
(如/dev/sda
)。 - udev规则:
udev
服务可通过USB设备的厂商ID(Vendor ID)和产品ID(Product ID)匹配规则,例如通过ENV{ID_VENDOR_ID}=="0781"
和ENV{ID_MODEL_ID}=="5581"
识别特定U盘,并设置ID_USB_DRIVER=usb-storage
等属性。 - 驱动类型:U盘由
usb-storage
或uas
驱动管理,而内置硬盘由ahci
(SATA)、sd_mod
(SCSI)等驱动管理,可通过/sys/block/sdb/device/
目录下的驱动名称区分。
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/17912.html