使用Golang重新实现dmidecode

李纳斯 · · 1150 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

背景

之前采集设备硬件信息使用的是命令行工具: dmidecode, 所以需要提前确认环境是否安装了该工具,而且有些嵌入式的环境, 并不方便安装工具, 所以需要用一个无依赖的库来替换.

github找了一圈, 发现并没有现成的,但幸运的是有一个读取smbios信息的库: go-smbios smbios信息读取库, 基于这个功能 再参考SMBIOS Reference 6 Specification就能写解析库了.

在开始解析数据之前, 我们先了解一些关于SMBIOS的基本概念.

什么是SMBIOS

SMBIOS是主板或系统制造者以标准格式显示产品管理信息所需遵循的统一规范, 由行业指导机构 Desktop Management Task Force (DMTF) 起草的开放性的技术标准.

他为上层系统, 比如DMI(Desktop Management Interface, DMI)提供了有关电脑系统和配件的信息.

因此我们要做的第一步就是: 读取SMBIOS信息.

如何读取SMBIOS信息

对于符合 SMBIOS 规范的计算机,可以通过访问 SMBIOS 的结构获得系统信息, 共有两种办法可以访问:

  1. 通过即插即用功能接口访问 SMBIOS 结构,这个在 SMBIOS2.0 标准里定义了,从 SMBIOS 2.1 开始这个访问方法不再被推荐使用
  2. 基于表结构的方法,表内容是 table entry point 的数据,这个访问方法从 SMBIOS 2.1 以后开始被使用,从 2.1 开始,以后的版本都推荐使用这种访问方式。在 2.1 版本中允许支持这两种方法中的任意一种和两种都支持,但在 2.2 以后的版本,必须支持方法 2 。在最新的 2.7.0 版中第一种方法已经废弃

鉴于市场上计算机已经均支持 SMBIOS2.3 标准,所以只考虑方法 2 ,基于表结构的访问方式。基于表结构访问 SMBIOS 的过程是先找到 Entry Point Structure ( EPS )表,然后通过 Entry Point Structure ( EPS )表的数据找到 SMBIOS 结构表

如何找到EPS表, 这里不做介绍, 具体请参考SMBIOS信息概述 -- DMI, 以下描述EPS表的数据结构

位置 名称 长度 描述
00H 关键字 4字节 固定是”SM
04H 校验和 1字节 用于校验数据
05H 表结构长度 1字节 Entry Point Structure 表的长度
06H Major 版本号 1字节 用于判断SMBIOS 版本
07H Minor 版本号 1字节 用于判断SMBIOS 版本
08H 表结构大小 2字节 用于即插即用接口方法获得数据表结构长度
0AH EPS 修正 1字节 -
0B-0FH 格式区域 5字节 存放解释EPS 修正的信息
10H 关键字 5字节 固定为“DMI
15H 校验和 1字节 Intermediate Entry Point Structure (IEPS)的校验和
16H 结构表长度 2字节 SMBIOS 结构表的长度
18H 结构表地址 4字节 SMBIOS 结构表的真实内存位置
1CH 结构表个数 2字节 SMBIOS 结构表数目
1EH Smbios BCD 修正 1字节 -

通过 EPS 表结构中 16H 以及 18H 处,得出数据表长度和数据表地址,即可通过地址访问 SMBIOS 数据结构表,以下描述SMBIOS数据结构表的格式

位置 名称 长度 描述
00H TYPE 号 1字节 结构的TYPE 号
01H 长度 1字节 本结构的长度,就此TYPE 号的结构而言
02H 句柄 2字节 用于获得本SMBIOS 结构,其值不定, 每个结构都分为格式区域和字符串区域,格式区域就是一些本结构的信息,字符串区域是紧随在格式区域后的一个区域

如此复杂, 谁能帮帮我?
对github上已经有人帮你完成了读取阶段的工作: go-smbios smbios信息读取库, 并且已经做好了操作系统的适配, 支持如下操作系统:

  • DragonFlyBSD (/dev/mem)
  • FreeBSD (/dev/mem)
  • Linux (sysfs and /dev/mem)
  • NetBSD (/dev/mem)
  • OpenBSD (/dev/mem)
  • Solaris (/dev/mem)
  • Windows (GetSystemFirmwareTable)

到此我们已经找到了SMBIOS 结构表, 并且了解了其数据结构, 剩下的就是如何解析了

如何解析SMBIOS信息

SMBIOS 结构表中TYPE号和长度是比较好解析的, 直接读取就好, 但是句柄的内容需要根据TYPE号查看文档进行解析, 这里说的解析 也主要指的是句柄(Handle)内容的解析

下面已TYPE0为列, 看看的他的数据结构:

位置 名称 长度 描述
00H TYPE 号 1字节 结构的TYPE 号,此处是0
01H 长度 1字节 TYPE 0 格式区域的长度,一般为14H ,也有13H
02H 句柄 2字节 本结构的句柄,一般为0000H
04H Bios 厂商信息 1字节 此处是bios 卖方的信息,可能是OEM 厂商名,一般为01H ,代表紧随格式区域后的字符串区域的第一个字符串
05H BIOS 版本 1字节 BIOS 版本号,一般为02H ,代表字符串区域的第二个字符串
06H Bios 开始地址段 2字节 用于计算常驻BIOS 镜像大小的计算,方法为(10000H-BIOS 开始地址段)×16
08H BIOS 发布日期 1字节 一般为03H ,表示字符区第三个字符串
09H BIOS rom size 1字节 计算方法为(n +1 )×64K ,n 为此处读出数值
0AH BIOS 特征 8字节 Bios 的功能支持特征,如PCI,PCMCIA,FLASH 等
12H Bios 特征扩展 不定 -

根据规则解析数据就可以了, 剩下的就是体力活了

来来来,张三你照着SMBIOS Reference6 Specification把数据解析下吧!

实现一个完整的dmidecode工具

体力活我已经帮你干了, 我已经按照SPEC文档, 进行了解析, 并且根据具体设备对照着dmidecode 写了测试用例, 因此你就放心使用吧!

package main

import (
    "fmt"
    "os"

    "github.com/yumaojun03/dmidecode"
)

func checkError(err error) {
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

func main() {
    dmi, err := dmidecode.New()
    checkError(err)

    infos, err := dmi.BIOS()
    // 支持以下类型的解析
    // dmi.BaseBoard()
    // dmi.Chassis()
    // dmi.MemoryArray()
    // dmi.MemoryDevice()
    // dmi.Onboard()
    // dmi.PortConnector()
    // dmi.Processor()
    // dmi.ProcessorCache()
    // dmi.Slot()
    // dmi.System()
    checkError(err)

    for i := range infos {
        fmt.Println(infos[i])
    }
}

有Bug请提Issue!, 当然如果可以 顺手点个Star(dmidecode)! 就差你的了

有Bug记得提Issue

参考

SMBIOS Reference6 Specification
SMBIOS信息概述 -- DMI
go-smbios smbios信息读取库
dmidecode Golang版实现


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:李纳斯

查看原文:使用Golang重新实现dmidecode

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1150 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传