
pciutils名称查找机制源码解析pci.ids、缓存、hwdb与DNS查询的完整链路【免费下载链接】pciutilsThe PCI Utilities项目地址: https://gitcode.com/gh_mirrors/pc/pciutils运行lspci时你能看到 Intel Corporation、NVIDIA GeForce 这样的友好名称而硬件寄存器里其实只有8086:1234这类 16 位十六进制数字。pciutilsThe PCI Utilities的名称查找机制就是把这些冷冰冰的 ID翻译成人类可读名称的完整链路。本文带你走通 lib/names.c 为核心的查找流程覆盖pci.ids 本地数据库、磁盘缓存、udev hwdb、DNS 远程查询四大数据源并给出实用的配置技巧。一、先搞懂一次名称查找要回答什么问题每个 PCI 设备由多组 ID 标识pciutils 把它们分成 7 类见 lib/names.h 中的enum id_entry_typeID 类型含义示例ID_VENDOR厂商10de NVIDIAID_DEVICE厂商 设备10de:00a3 GeForceID_SUBSYSTEM具体子系统4 个 ID 组合板卡厂商的定制型号ID_GEN_SUBSYSTEM通用子系统跨设备的通用分类ID_CLASS/ID_SUBCLASS大类 / 小类0300 VGA 控制器ID_PROGIF编程接口类的进一步细分而名字从哪里来答案是一个带优先级的四数据源体系每个条目在哈希表中都会被打上来源标记enum id_entry_srcSRC_LOCALpci.ids 本地文件 SRC_HWDBudev hwdb SRC_NETDNS SRC_CACHE磁盘缓存数值越大优先级越高这个规则直接写在 lib/names-hash.c 的pci_id_lookup()里if (!best || best-src n-src) best n;。二、数据源 1本地数据库 pci.ids 及其解析pci.ids 是业界标准的 PCI ID 数据库采用Tab 缩进表达层级的纯文本格式厂商名顶格设备名缩进一级子系统名缩进两级C xx开头的块描述 PCI 大类S xxxx块描述通用子系统。解析逻辑全部在 lib/names-parse.c 中pci_load_name_list()打开 ID 文件编译时由 configure 决定默认路径IDSDIR/pci.ids若启用PCI_COMPRESSED_IDS则自动尝试.gz压缩格式id_parse_list()逐行扫描按缩进层级nest 0/1/2/3维护一个当前类别状态机把每行拆解成cat id1~id4 名称统一调用pci_id_insert()以SRC_LOCAL写入哈希表遇到重复条目、超行长、未知块RFU都会返回精确的解析错误信息方便排查损坏的数据库文件。数据库如何保持新鲜项目自带的 update-pciids.sh 脚本会从上游站点下载最新的pci.ids自动选择 xz/bzip2/gzip 压缩与 curl/wget/lynx 下载器是日常维护的第一选择。三、核心数据结构4099 桶哈希表所有数据源查到的名字最终都汇入同一张内存哈希表实现在 lib/names-hash.c桶数量HASH_SIZE 4099素数减少哈希冲突内存分配按 8KB 的id_bucket大块分配再切分避免海量小malloc加载几 MB 的 pci.ids 也足够高效键的设计id_pair()把两个 16 位 ID 拼进一个 32 位整数id12、id34配合类别号做简单异或哈希来源标记每个id_entry携带src字段使同一个 ID 来自不同数据源的结果能在查询时按优先级裁决。这意味着pci.ids、缓存、hwdb、DNS 是合流的——无论哪个源先命中都插入同一张表后续查询直接命中内存不再重复查文件、系统库或网络。四、数据源 2磁盘缓存~/.cache/pci-idsDNS 查到的结果如果只用一次就扔掉太浪费了。lib/names-cache.c 为此提供了用户级磁盘缓存默认位置$XDG_CACHE_HOME/pci-ids未设置时回落到~/.cache/pci-ids由 lib/init.c 中pci_init_dns()初始化可用net.cache_name参数覆盖还支持~/展开文件格式首行是版本标记#PCI-CACHE-1.0其后每行类别 id1 id2 id3 id4 名称加载时逐行sscanf后以SRC_CACHE入表懒加载只有哈希表未命中且开启了PCI_LOOKUP_CACHE时pci_id_cache_load()才会被触发原子写入进程退出时pci_id_cache_flush()先把条目写到xxx.tmp-主机名-PID临时文件再rename覆盖避免多进程写坏缓存只有SRC_CACHE/SRC_NET来源且非空的条目会被落盘本地文件本身就有不必重复保存刷新机制每次 DNS 命中都会调用pci_id_cache_dirty()把状态置脏保证新结果最终写入磁盘PCI_LOOKUP_REFRESH_CACHE标志则跳过读缓存、强制全量刷新。五、数据源 3udev hwdb发行版硬件数据库现代 Linux 发行版通常自带 systemd 的 hwdb 硬件数据库pciutils 会顺手利用它实现在 lib/names-hwdb.c用modalias 字符串查询例如厂商用pci:v10DE0000*、设备用pci:v10DE0000d00A30000*不同 ID 类型对应不同的 hwdb 键ID_VENDOR_FROM_DATABASE、ID_MODEL_FROM_DATABASE、ID_PCI_CLASS_FROM_DATABASE等首次查询时才udev_new()udev_hwdb_new()初始化句柄用完由pci_id_hwdb_free()释放已知限制hwdb 没有子系统对应的键所以ID_SUBSYSTEM/ID_GEN_SUBSYSTEM直接返回 NULL想关闭它设置hwdb.disable1参数即可或者编译时不带PCI_HAVE_HWDB自动降级为空实现。查到的名字会以SRC_HWDB写回哈希表优先级仅次于 pci.ids 本地文件。六、数据源 4DNS 远程查询TXT 记录这是 pciutils 最黑科技的设计——用 DNS TXT 记录当在线 ID 数据库实现在 lib/names-net.c构造域名把 ID 按固定格式拼成主机名再拼上查询域默认pci.id.ucw.cz由 lib/configure 写入PCI_ID_DOMAIN可用net.domain参数覆盖ID 类型DNS 主机名格式示例厂商%04x10de.pci.id.ucw.cz设备%04x.%04x00a3.10de.pci.id.ucw.cz子系统%04x.%04x.%04x.%04x1234.5678.00a3.10de...通用子系统%04x.%04x.s1234.5678.s...大类/小类/接口%02x.c/%02x.%02x.c/%02x.%02x.%02x.c03.c...发查询用系统解析器res_query()请求 IN TXT 记录手工解析应答由于缺少可移植的 DNS RR 解析 API代码用GET16/GET32宏按位手工拆包跳过 Question 区、逐条读 Answer 区再从 TXT 各字符串中找以i开头的项——iNVIDIA Corporation就是最终名称负缓存查询无结果时会插入一个空名称条目pci_id_insert(..., , SRC_NET)后续同一 ID 直接命中哈希表不会反复发网络请求。 注意DNS 与缓存功能都受PCI_USE_DNS编译开关控制默认构建通常未启用需要在 configure 时打开。七、完整链路串讲id_lookup() 的查找流程把所有环节串起来的就是 lib/names.c 中的id_lookup()它是一个先查表、未命中就逐级补充数据源的 while 循环pci_lookup_name()对外 APIlib/names.c │ ├─ ① 首次调用pci_load_name_list() 加载 pci.ids → SRC_LOCAL 入哈希表 │ └─ id_lookup() 循环 ├─ 查哈希表命中 ──是── 直接返回 │ 否 ├─ ② 缓存未加载 pci_id_cache_load() 读 ~/.cache/pci-ids → SRC_CACHE 入表continue ├─ ③ 还没试过 hwdb pci_id_hwdb_lookup() → 命中则 SRC_HWDB 入表continue ├─ ④ 开了 PCI_LOOKUP_NETWORK pci_id_net_lookup() DNS 查询 │ 命中 → SRC_NET 入表 标记缓存脏未命中 → 空条目入表负缓存 └─ ⑤ 所有源都失败 → 返回 NULL显示 Unknown vendor [8086]三个值得注意的细节三级子系统回退id_lookup_subsys()先查设备专属子系统名再查通用子系统名如果子系统 ID 恰好等于设备 ID还会回退用设备名顶替——这就是 lspci 中很多设备能显示合理名称的作弊逻辑输出格式由 flags 控制PCI_LOOKUP_NUMERIC纯数字、PCI_LOOKUP_MIXED名称 [数字]、PCI_LOOKUP_NO_NUMBERS纯名称对应lspci、lspci -n、lspci -nn等不同输出风格IDE 特例ID_PROGIF对0101类IDE 控制器会手工组合 Master/Secondary 等位标记这是规范语义太复杂导致的硬编码分支。上层工具 lspci.c、ls-tree.c、ls-caps.c 都只调用pci_lookup_name()这一个公开 API见 lib/pci.h底层四大源对它们完全透明。八、实战技巧让 lspci 认出更多设备名1. 保持本地数据库最新最推荐# 克隆源码如需阅读本机制的完整实现 git clone https://gitcode.com/gh_mirrors/pc/pciutils cd pciutils sh update-pciids.sh -q # 更新 ./pci.ids更新后以--id-file指定或在包管理器层面更新系统自带的 pci.ids。2. 启用 DNS 磁盘缓存编译时开启PCI_USE_DNS后可配合参数net.domain自定义查询域默认pci.id.ucw.cznet.cache_name自定义缓存路径默认~/.cache/pci-idsDNS 结果会自动沉淀到磁盘缓存第二次运行连网都不用。3. 控制数据源行为参数 / 标志作用hwdb.disable1跳过 udev hwdb 查询PCI_LOOKUP_SKIP_LOCAL忽略 pci.ids 本地条目调试用PCI_LOOKUP_NO_HWDB禁用 hwdb 源PCI_LOOKUP_REFRESH_CACHE不读旧缓存强制全量刷新lspci -n / -nn纯数字输出 / 名称数字混排九、核心文件速查表 文件职责lib/names.c对外 APIpci_lookup_name()与主查找循环id_lookup()lib/names.hID 类型、来源枚举、哈希表参数定义lib/names-parse.cpci.ids 文件格式解析含 .gz 支持lib/names-hash.c4099 桶哈希表按来源优先级裁决lib/names-cache.c~/.cache/pci-ids磁盘缓存的读写lib/names-net.cDNS TXT 远程查询与手工应答解析lib/names-hwdb.cudev hwdb 查询集成update-pciids.sh在线更新 pci.ids 数据库脚本总结pciutils 的名称查找机制是一次教科书级的多级数据源 统一哈希表设计pci.ids 保证离线可用且优先级最高udev hwdb 复用发行版已有的成果DNS 提供在线兜底磁盘缓存让网络结果持久化——四者通过带来源标记的哈希表合流用 O(1) 查询和逐级回退兼顾了速度、准确与离线场景。理解了这条完整链路你就能自由定制 lspci 的名称解析行为也为学习其他本地→系统→网络分层查找系统提供了优秀范本。【免费下载链接】pciutilsThe PCI Utilities项目地址: https://gitcode.com/gh_mirrors/pc/pciutils创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考