背景
在做硬件采集的时候,需要采集设备的基础信息,例如:厂商、型号等等,那我们就要考虑是否有统一的方法来获取这些信息呢?显然肯定是有的,在此我将介绍具体实现方法。文章介绍了sysfs相关知识,例如modalias,以及硬件采集的具体应用。
sysfs介绍
linux内核开始使用sysfs文件系统,它的作用是将设备和驱动程序的信息导出到用户空间,方便了用户读取设备信息,同时支持修改和调整。
/sys/block/
记录每个块设备的一个符号链接,符号链接指向/sys/devices/下,例如
$ ls -l /sys/block/
lrwxrwxrwx 1 root root 0 Nov 3 16:11 sda -> ../devices/pci0000:00/0000:00:01.0/0000:01:00.0/host0/target0:0:11/0:0:11:0/block/sda
lrwxrwxrwx 1 root root 0 Nov 3 16:11 sdb -> ../devices/pci0000:00/0000:00:01.0/0000:01:00.0/host0/target0:0:30/0:0:30:0/block/sdb
……
lrwxrwxrwx 1 root root 0 Nov 3 16:11 sdt -> ../devices/pci0000:00/0000:00:01.0/0000:01:00.0/host0/target0:0:28/0:0:28:0/block/sdt
lrwxrwxrwx 1 root root 0 Nov 3 16:11 sdu -> ../devices/pci0000:00/0000:00:01.0/0000:01:00.0/host0/target0:0:29/0:0:29:0/block/sdu
lsscsi
lsscsi我们再熟悉不过了,在更换磁盘的时候经常会用来确定磁盘和VD的对应关系,我们口口相传倒数第二条是VD号,谁也没说清是为什么。其实这些条目分别是H:C:T:L,target对应的就是VD号。
<h:c:t:l> filter output list (def: '*:*:*:*' (all)). Meaning:
<host_num:controller:target:lun> or for NVMe:
<'N':ctl_num:cntlid:namespace_id>
同样的,lsscsi的第三段会看到许多不一样的结果,这些是传输协议,例如ATA、SATA、DELL等等
$ lsscsi
[0:0:0:0] disk ATA INTEL SSDSC2KB01 0121 /dev/sda # JBOD
[0:0:1:0] disk ATA INTEL SSDSC2KB01 0121 /dev/sdb
……
[0:2:0:0] disk DELL PERC H730 Mini 4.25 /dev/sdu # RAID1
$ lsscsi
[0:0:1:0] disk ATA SAMSUNG MZ7L3480 104Q - # 同一个NVME会出现多次?
[0:0:2:0] disk ATA SAMSUNG MZ7L3480 104Q - # 同一个NVME会出现多次?
[0:1:0:0] disk LSI Logical Volume 3000 /dev/sda
[N:0:6:1] disk SAMSUNG MZQL21T9HCJR-00B7C__1 /dev/nvme0n1 # 同一个NVME会出现多次?
常见的传输协议有:IEEE 1394, ATA, FC, iSCSI, SAS, SATA, SPI and USB
什么是 ATA 和 SATA
ATA 也称为 IDE(集成驱动器电子系统),是一种较老的接口标准,于 20 世纪 80 年代推出。它在早期 PC 开发中发挥了关键作用,用于连接硬盘和光驱等存储设备。ATA 使用并行数据传输,使用多条数据线同时传输数据。存储设备通过 16 位并行连接与计算机主板相连。在标准设置中,每个通道启用两个设备。ATA 使用主从配置,最多允许四个设备。
SATA是串行 ATA 的缩写,是 21 世纪初推出的一种较新的接口标准。它取代了高级技术附件或 IDE 等较旧的接口标准。SATA 解决了以前并行数据传输方法的局限性,从而提高了性能、电缆管理和效率。使用串行连接可以实现比 ATA 更快的数据传输速率,速度高达 6 Gb/s。SATA 接口的每个通道都支持单个设备。但是,大多数现代主板上都有多个 SATA 端口,可以连接多个设备。
代码实现
那么我们如何获取到HCTL呢?
$ ls /sys/block/sd*/device/bsg/
0:2:0:0
vd和block关联
// vd和block关联
func (m *Megacli) virtualDiskToBlockDevice(pci, vd string) (*block.Block, error) {
var err error
if m.Block == nil {
m.Block, err = block.Blocks()
if err != nil {
return nil, err
}
}
for _, b := range m.Block {
vendor := b.Vendor
if vendor == "ATA" {
// ATA设备不是RAID设备
continue
}
bus_path := b.BusPath
hctl := b.HCTL
parts := strings.Split(hctl, ":")
if len(parts) == 4 {
if string(parts[2]) == vd && strings.Contains(bus_path, pci) {
return b, nil
}
}
}
return nil, nil
}
sysfs-modalias 硬件信息
wiki.archlinux.org/title/Modal…
Modalias是一个sysfs小技巧,它将硬件信息导出到一个名为modalias的文件中。
$ cat /sys/devices/pci0000:00/0000:00:1f.1/modalias
pci:v00008086d000024DBsv0000103Csd0000006Abc01sc01i8A
路径解释
-
pci0000:00: PCI ID -
0000:00:1f.1:PCI总线上的索引,位于总线0000:00上,索引为1f.1,例如:-
$ lspci |grep 1f.1 00:1f.1 IDE interface: Intel Corp.: Unknown device 24db (rev 02)
-
内容解释
根据供应商id和设备id可以在devicehunt.com/查找到相关信息
v 00008086 # vendor id 供应商id
d 000024DB # device id 设备id
sv 0000103C # subsystem vendor id 子系统供应商id (通常被忽略)
sd 0000006A # subsystem device id 子系统设备id (通常被忽略)
bc 01 # base class 基类
sc 01 # sub class 子类
i 8A # 编程接口
对应lspci
$ lspci -n |grep 1f.1
00:1f.1 Class 0101: 8086:24db (rev 02) # Class 0101对应IDE Interface
# 8086对应Intel
# 24db对应Unknown device,其实就是lspci的内容
运行depmod时,它会在/lib/modules/$(uname -r)/中构建一系列“映射”文件(如何构建的呢?就是从内核驱动中获取的),这些文件告诉modprobe如何处理它需要做的某些事情。
$ sudo grep megaraid_sas /lib/modules/$(uname -r)/modules.alias
alias pci:v00001000d0000001Csv*sd*bc*sc*i* megaraid_sas
alias pci:v00001000d0000001Bsv*sd*bc*sc*i* megaraid_sas
alias pci:v00001000d00000017sv*sd*bc*sc*i* megaraid_sas
alias pci:v00001000d00000016sv*sd*bc*sc*i* megaraid_sas
alias pci:v00001000d00000014sv*sd*bc*sc*i* megaraid_sas
alias pci:v00001000d00000053sv*sd*bc*sc*i* megaraid_sas
# 这些信息就是从驱动里面动态载入的
$ sudo modinfo megaraid_sas
filename: /lib/modules/4.9.0-8-amd64/kernel/drivers/scsi/megaraid/megaraid_sas.ko
description: Avago MegaRAID SAS Driver
author: megaraidlinux.pdl@avagotech.com
version: 07.700.00.00-rc1
license: GPL
srcversion: 470A16FC7EEFFB94387E241
alias: pci:v00001000d0000001Csv*sd*bc*sc*i*
alias: pci:v00001000d0000001Bsv*sd*bc*sc*i*
alias: pci:v00001000d00000017sv*sd*bc*sc*i*
alias: pci:v00001000d00000016sv*sd*bc*sc*i*
alias: pci:v00001000d00000014sv*sd*bc*sc*i*
alias: pci:v00001000d00000053sv*sd*bc*sc*i*
alias: pci:v00001000d00000052sv*sd*bc*sc*i*
代码实现
编程时,我们有了modalias信息,就可以通过一些库来获取到设备信息,而不用使用各种cli来获取,一次io操作提高效率,例如:pcidb
type PCIDB struct {
// hash of class ID -> class information
Classes map[string]*Class `json:"classes"`
// hash of vendor ID -> vendor information
Vendors map[string]*Vendor `json:"vendors"`
// hash of vendor ID + product/device ID -> product information
Products map[string]*Product `json:"products"`
}
var db *pcidb.PCIDB
vendor := db.Vendors[vendorID]
fmt.Println(vendor.Name) // 厂商
product := db.Products[vendorID+productID]
fmt.Println(product.Name) // 产品