4. SMBIOS 数据结构基础
SMBIOS 的核心是标准化的数据结构。每个结构都遵循统一的头部格式、类型编号规则、字符串区域机制以及表结束标记。理解这些基础是解析和开发 SMBIOS 功能的第一步。
4.1 Structure Header 格式
每个 SMBIOS 结构都以一个 4 字节的固定头部(Header)开始。这个头部是所有结构类型的共同起点,定义了结构的类型、长度和句柄。
| 偏移量 (Offset) | 字节数 (Size) | 字段名 | 描述 |
|---|---|---|---|
| 00h | 1 | Type | 结构类型编号,标识该结构代表的信息类别(如 BIOS、系统信息、内存设备等)。 |
| 01h | 1 | Length | 结构格式化区域的长度(以字节为单位)。该长度不包括字符串区域。 |
| 02h | 2 | Handle | 结构的唯一标识符。用于在结构之间建立引用关系(例如,内存设备引用其所属的内存控制器)。 |
关键点:
Length 字段仅包含格式化数据部分的长度。要访问紧随其后的字符串区域,必须跳过 Length 指定的字节数。
4.2 Type 编号规则
SMBIOS 规范为每种信息类别分配了一个唯一的 Type 编号。编号范围从 0 到 127 由规范定义,128 到 255 保留给 OEM 或特定实现使用。
以下是常见且重要的 Type 编号及其含义:
| Type | 名称 | 描述 |
|---|---|---|
| 0 | BIOS Information | BIOS 供应商、版本、起始地址、运行大小等。 |
| 1 | System Information | 制造商、产品名称、版本、序列号、UUID。 |
| 2 | Baseboard (or Module) Information | 主板制造商、产品、版本、序列号。 |
| 3 | System Enclosure or Chassis | 机箱类型、制造商、版本、序列号、资产标签。 |
| 4 | Processor Information | CPU 类型、家族、制造商、最大/当前速度、核心数。 |
| 7 | Cache Information | 缓存配置、大小、类型、支持的模式。 |
| 16 | Physical Memory Array | 物理内存阵列的位置、用途、最大容量、错误修正类型。 |
| 17 | Memory Device | 具体内存条的大小、类型、速度、制造商、序列号。 |
| 127 | End-of-Table | SMBIOS 结构表的终止标记。 |
开发提示: 在解析 SMBIOS 表时,通常通过
Type 字段来筛选感兴趣的结构。例如,获取 CPU 信息时,只处理 Type == 4 的结构。
4.3 String 区域机制
SMBIOS 结构中的文本信息(如制造商名称、版本号)并不直接存储在格式化区域中,而是通过 字符串区域(String Area) 来管理。这是一种节省空间且灵活的机制。
- 存储方式: 格式化区域中的字符串字段存储的是一个 字符串索引号(从 1 开始)。
- 字符串区域位置: 紧跟在格式化区域之后(即偏移量为
Length的位置开始)。 - 字符串格式: 由一系列以
null(0x00) 结尾的 ASCII 字符串组成。 - 访问规则: 要获取某个字段的字符串值,先读取其索引号
n,然后在字符串区域中跳过前n-1个null结尾的字符串,第n个字符串即为所需内容。 - 空字符串: 如果索引号为 0,则表示该字段没有可用的字符串信息。
常见错误: 字符串索引从 1 开始,而不是 0。索引 0 是一个特殊值,表示“无字符串”。直接使用索引 0 去字符串区域查找会导致解析错误。
4.4 End-of-Table 标记
SMBIOS 结构表不是无限延伸的。它以一个特殊的 End-of-Table 结构作为结束标记。这个结构的 Type 编号为 127。
End-of-Table 结构的特点:
- Type: 固定为 127 (0x7F)。
- Length: 固定为 4 字节(仅包含头部,没有格式化数据)。
- Handle: 固定为 0xFFFF(表示无效句柄)。
- 字符串区域: 不存在字符串区域。
在遍历 SMBIOS 结构表时,当遇到 Type == 127 的结构,应立即停止解析,因为其后不再有有效的结构数据。
4.5 结构布局总览
下面的 SVG 图展示了 SMBIOS 结构在内存中的完整布局,从头部到字符串区域,并以 End-of-Table 结构结束。
4.6 字符串索引访问示例
下面的 SVG 图展示了如何通过索引号在字符串区域中定位具体的字符串。
4.7 解析流程总结
解析一个 SMBIOS 结构的标准流程如下:
- 定位结构: 从 SMBIOS Entry Point 获取结构表起始地址。
- 读取头部: 读取前 4 字节,获取
Type、Length和Handle。 - 检查结束: 如果
Type == 127,则解析结束。 - 处理格式化数据: 根据
Type对应的结构定义,解析从偏移量 4 开始的Length - 4字节数据。 - 解析字符串: 从偏移量
Length开始,遍历字符串区域,根据索引号提取文本。 - 跳转到下一个结构: 跳过当前结构的格式化区域和整个字符串区域(直到遇到两个连续的
null字节),然后重复步骤 2。
代码示例: 以下 C 语言代码片段展示了如何遍历 SMBIOS 结构并打印其 Type 和 Handle。
// 假设 smbios_table 指向结构表起始地址
uint8_t* current = smbios_table;
SMBIOS_HEADER* header;
while (1) {
header = (SMBIOS_HEADER*)current;
// 检查 End-of-Table
if (header->type == 127) {
break;
}
printf("Type: %d, Handle: %d\n", header->type, header->handle);
// 计算字符串区域起始地址
uint8_t* string_area = current + header->length;
// 查找字符串区域结束位置(两个连续的 null 字节)
uint8_t* end = string_area;
while (!(*end == 0 && *(end+1) == 0)) {
end++;
}
end += 2; // 跳过双 null 终止符
// 移动到下一个结构
current = end;
}