dmidecode命令主要是通过DMI获取主机的硬件信息,其输出的信息包括BIOS、系统、主板、处理器、内存、缓存等等。它是通过SMBIOS(System Management BIOS)来获取信息的。SMBIOS是主板或系统制造者以标准格式显示产品管理信息所需遵循的统一规范。
什么是DMI?
DMI (Desktop Management Interface, DMI)的主要组成部分是Management InformationFormat (MIF)数据库,这个数据库包括了所有有关电脑系统和配件的信息。
百度百科到的资料:
DMI是指Direct Media Interface(直接媒体接口)的缩写,DMI是Intel(英特尔)公司开发用于连接主板南北桥的总线,取代了以前的Hub-Link总线。DMI采用点对点的连接方式,时钟频率为100MHz,由于它是基于PCI-Express总线,同样采用8bit/10bit(有效位宽8bit)编码因此具有PCI-E总线的优势。
在4系列芯片组没有取消前端总线FSB时,DMI 是Intel(英特尔)公司开发用于北桥(G)MCH(Graphics & Memory controller hub)和南桥ICH10/ICH7之间的芯片连接总线。DMI实现了上行与下行双向数据传输率,单通道单向传输速率达到2.5GT/s,采用8bit/10bit编码,共计4条通道。这个高速接口集成了高级优先服务,允许并发通讯和真正的同步传输能力。它的基本功能对于软件是完全透明的,因此早期的软件也可以正常操作。
从5系列芯片组开始的新构架设计中,前端总线被取消,北桥芯片的功能被整合进CPU中。显卡采用了PCI-E ×16的通道直连CPU,当多卡交火时分为×8+×8(双卡)或×8+×4+×4(三卡)(具体分配方式要参考主板设计)。因为PCI-E2.0的应用,DMI升级到DMI2.0,单通道单向传输速率达到5GT/s。同时DMI2.0也不再用于南北桥芯片的连接,而是用于CPU和芯片组(原南桥芯片组)的连接。如下图所示,下图展示的是北桥集成到CPU后的情况:
所以,我们现在可以知道,为何dmidecode工具可以获取主板,内存等信息了,因为很多关于系统硬件的底层信息都是通过DMI通道获取的,我们看一下dmidecode工具的帮助信息:
dmidecode 工具会读取/sys/firmware/dmi/tables/smbios_entry_point文件。
czl@czl-Vostro-3268:~$ sudo strace -e trace=open,close,openat dmidecode
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
close(3) = 0
# dmidecode 3.1
openat(AT_FDCWD, "/sys/firmware/dmi/tables/smbios_entry_point", O_RDONLY) = 3
close(3) = 0
Getting SMBIOS data from sysfs.
SMBIOS 3.0.0 present.
Table at 0x000E0000.
openat(AT_FDCWD, "/sys/firmware/dmi/tables/DMI", O_RDONLY) = 3
close(3) = 0
dmidecode的常见用法:
$ dmidecode # 打印所有硬件信息
$ dmidecode -q # 打印所有硬件信息,比较简洁
$ dmidecode -h # 获取帮助
$ dmidecode | grep 'Product Name' # 以过滤的方式来查看指定的硬件信息
$ dmidecode --type bios # 查看BIOS相关的硬件信息
$ dmidecode --type system # 查看系统相关的硬件信息
$ dmidecode --type baseboard # 查看主板相关的硬件信息
$ dmidecode --type chassis # 查看机箱相关的硬件信息
$ dmidecode --type processor # 查看处理器相关的硬件信息
$ dmidecode --type memory # 查看内存相关的硬件信息
$ dmidecode |grep 'Serial Number' # 查看主板的序列号
$ dmidecode -s system-serial-number # 查看系统序列号
$ dmidecode -t 11 # 查看OEM信息
$ dmidecode -t 7 # 查看L1/L2/L3 Cache信息
$ dmidecode -t 4 # 查看CPU信息
$ dmidecode -t slot #查看PCIE Slot槽位信息
take get pcie slot for example
czl@czl-Vostro-3268:~$ sudo dmidecode -t slot
# dmidecode 3.1
Getting SMBIOS data from sysfs.
SMBIOS 3.0.0 present.
Handle 0x0024, DMI type 9, 17 bytes
System Slot Information
Designation: SLOT1
Type: x1 PCI Express
Current Usage: Available
Length: Short
ID: 1
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:ff:1f.7
Handle 0x0025, DMI type 9, 17 bytes
System Slot Information
Designation: SLOT2
Type: x16 PCI Express 3
Current Usage: Available
Length: Long
ID: 2
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:ff:1f.7
dmidecode打开文件
sudo strace -tt -T -v -f -e trace=file -o ./strace.log dmidecode
对应的内核代码在文件drivers/firmware/dmi_scan.c中:
获取机器型号的方法
method 1:
dmesg
method 2:
sudo dmidecode -t 1
内核中获取dmi信息
前面提到,用户空间dmidecode工具是通过/sys/firmware/dmi/tables/smbios_entry_point文件来获取dmi信息的,sysfs是内核空间创建的一个内存文件系统,则必然在内核中也有完备的dmi信息,那么在内核中如何获取呢?
在dmi的内核实现文件dmi_scan.c中,能找到如下几个接口,它们有被作为开放接口export出去,分别是:
u64 dmi_memdev_size(u16 handle);
void dmi_memdev_name(u16 handle, const char **bank, const char **device);
bool dmi_match(enum dmi_field f, const char *str);
int dmi_walk(void (*decode)(const struct dmi_header *, void *);
int dmi_get_bios_year(void);
bool dmi_get_date(int field, int *yearp, int *monthp, int *dayp);
const struct dmi_device *dmi_find_device(int type, const char *name,const struct dmi_device *from);
int dmi_name_in_vendors(const char *str);
const char *dmi_get_system_info(int field);
const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list);
int dmi_check_system(const struct dmi_system_id *list);
其中,dmi_get_system_info就是我们要找的,它的参数包含如下类型,在内核中被广泛使用:
原理
dmi数据存储在哪里?
dmi数据是二进制数据,需要通过架构的IO操作读取到内核,内核为此创建了一个32字节的全局buffer保存,叫做static u8 smbios_entry_point[32];
关键逻辑是首先调用dmi_early_remap对存储smbios信息的区域进行映射,拿到虚拟地址后再dmi_smbios3_present/dmi_present中调用memcpy_fromio读进dmi_early_remap buffer中。最后设置dmi_available为1。
dmidecode就是将smbios_entry_point读取到用户空间解码发挥作用的。
execve("/usr/sbin/dmidecode", ["dmidecode"], 0x7fff725b3940 /* 26 vars */) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\35\2\0\0\0\0\0"..., 832) = 832
close(3) = 0
openat(AT_FDCWD, "/sys/firmware/dmi/tables/smbios_entry_point", O_RDONLY) = 3
read(3, "_SM_\7\37\2\7s\0\0\0\0\0\0\0_DMI_\327D*\20\0\16\0\4\1\0", 32) = 31
read(3, "", 1) = 0
close(3) = 0
openat(AT_FDCWD, "/sys/firmware/dmi/tables/DMI", O_RDONLY) = 3
read(3, "\0\30\0\0\1\2I\352\3\0\220\337\t|\0\0\0\0\201\7\4\6\0\0Phoenix "..., 10820) = 4096
read(3, "RAM slot #33\0\0\21\"]\0:\0\377\377\0\0\0\0\0\0\t\0\1\1"..., 6724) = 4096
read(3, "\0\0\0\0\0NVD #61\0\0\21\"\271\0%\0\377\377 \0 \0\0\0\t\0\1\1"..., 2628) = 2628
read(3, "", 0) = 0
close(3) = 0
+++ exited with 0 +++
注意其中的raw_table_read函数实现。
除此之外,dmi仅仅是一个 sysfs中创建的子目录
callstack
[ 129.107667] Hardware name: Dell Inc. Vostro 3268/0TJYKK, BIOS 1.11.1 12/11/2018
[ 129.107667] Call Trace:
[ 129.107669] dump_stack+0x6d/0x8b
[ 129.107670] raw_table_read+0x24/0x30
[ 129.107672] sysfs_kf_bin_read+0x4d/0x80
[ 129.107673] kernfs_fop_read+0xad/0x1a0
[ 129.107675] __vfs_read+0x1b/0x40
[ 129.107676] vfs_read+0x8e/0x130
[ 129.107678] ksys_read+0xa7/0xe0
[ 129.107679] __x64_sys_read+0x1a/0x20
[ 129.107681] do_syscall_64+0x57/0x190
[ 129.107682] entry_SYSCALL_64_after_hwframe+0x5c/0xc1
[ 129.107683] RIP: 0033:0x7f01e2dd1031
参考资料
https://blog.csdn.net/zhangliang19950813/article/details/105763801