lscpu命令相关深入
- 一、概述
- 1. lscpu 源码
- 2. 源码文件位置
- 3. 基本流程
- 二、/proc/cpuinfo
- 三、sysfs cpu
- 四、dmidecode
一、概述
在大多数 Linux 发行版中,lscpu 是 util-linux 软件包的一部分
1. lscpu 源码
util-linux 的主仓库在 GitHub:
gitclone https://github.com/util-linux/util-linux.gitcdutil-linux直接查看在线源码
GitHub 浏览:https://github.com/util-linux/util-linux/blob/master/sys-utils/lscpu.c
Kernel.org 镜像:https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/
2. 源码文件位置
在 util-linux 项目中,相关文件主要有:
- sys-utils/lscpu.c - 主程序源码
- sys-utils/lscpu.h - 头文件
- sys-utils/lscpu-*.c - 其他相关文件
3. 基本流程
1.初始化阶段
- 创建上下文结构
- 初始化路径(/sys、/proc、根文件系统)
2. 信息收集阶段
按顺序调用读取函数:
- lscpu_read_cpulists() - 读取 CPU 列表(possible、present、online)
- lscpu_read_cpuinfo() - 解析 /proc/cpuinfo
- lscpu_read_architecture() - 获取架构信息
- lscpu_read_archext() - 读取架构扩展信息
- lscpu_read_vulnerabilities() - 读取 CPU 漏洞信息
- lscpu_read_numas() - 读取 NUMA 信息
- lscpu_read_topology() - 读取拓扑信息(核心、插槽、缓存等) lscpu-topology.c:692-730
- lscpu_read_virtualization() - 读取虚拟化信息
3. 输出阶段
根据指定的输出模式(–summary、–caches、–extended、–parse)格式化并输出结果
- LSCPU_OUTPUT_SUMMARY - 默认摘要输出
- LSCPU_OUTPUT_CACHES - 缓存详细信息
- LSCPU_OUTPUT_READABLE - 人类可读的表格格式
- LSCPU_OUTPUT_PARSABLE - 机器可解析格式
4. 核心匹配机制
lscpu 命令通过分阶段读取和解析 /proc/cpuinfo 和 /sysfs 来匹配 CPU 信息。
lscpu 首先从 /proc/cpuinfo 读取 CPU 基本类型信息,然后从 /sys/devices/system/cpu/ 读取拓扑信息,最后通过逻辑 CPU ID 进行匹配
详细流程:
1. 读取 /proc/cpuinfo
lscpu_read_cpuinfo() 函数解析 /proc/cpuinfo 文件,使用模式匹配来识别不同架构的字段- 定义了三种模式:CPUINFO_LINE_CPUTYPE、CPUINFO_LINE_CPU、CPUINFO_LINE_CACHE
- 使用 cpuinfo_pattern 结构体映射字段名到内部结构体偏移
- 通过 processor 字段识别 CPU ID 并切换当前 CPU 上下文
2. 读取 /sysfs 拓扑信息
lscpu_read_topology() 函数从 /sys/devices/system/cpu/ 读取拓扑信息- 为每个 CPU 读取 core_id、socket_id、book_id、drawer_id
- 读取缓存信息和共享映射
- 读取频率信息(从 cpufreq 子系统)
3. 匹配机制
匹配通过逻辑 CPU ID 实现:- /proc/cpuinfo 中的 processor 字段提供逻辑 CPU ID
- /sysfs 中的路径 cpu%d/ 使用相同的逻辑 CPU ID
- 两个数据源的信息通过 lscpu_cpu 结构体关联
二、/proc/cpuinfo
/proc/cpuinfo 是由Linux 内核动态生成的虚拟文件,里面的信息是内核通过直接向CPU询问(执行 CPUID 等指令)和读取系统寄存器等底层操作实时获取的。
你可以把它理解为 Linux 内核在与 CPU “对话”后,实时整理的一份“身份证”和“能力清单”。
内核主要通过以下两种核心途径获取信息:
- CPUID 指令(x86/x86-64架构的核心来源)
这是最根本的方法。当内核(或任何程序)执行这条指令时,CPU会直接返回关于自身的信息。
工作方式:程序将查询代码放入 EAX 寄存器,然后执行 CPUID 指令,CPU会将结果填充到 EAX、EBX、ECX、EDX 寄存器中。
获取的信息:包括 vendor_id(厂商字符串,如 “GenuineIntel”)、cpu family、model、stepping(用于识别微架构步进)、model name 字符串(如 “Intel® Core™ i7-12700K”),以及所有支持的特性标志位(如 mmx、sse、avx、vmx 等)。
- 读取特定模型寄存器
对于某些无法通过 CPUID 获取的非常具体或新引入的硬件信息,内核会直接读取 MSR 等特定模型寄存器。
- 与 dmidecode 信息的本质区别
理解这一点至关重要,它解释了为何有时两者的信息会有差异:
| 特性 | /proc/cpuinfo | dmidecode -t 4 |
|---|---|---|
| 信息提供者 | CPU 硬件本身 | 主板 BIOS/UEFI 固件 |
| 获取方式 | 内核执行 CPUID 指令直接询问 | 从固件存储在内存中的 SMBIOS/DMI 表 中解码 |
| 实时性 | 实时,每次读取都重新查询 | 静态,是开机时固件写入内存的快照 |
| 准确性 | 极高,是CPU的“自述” | 依赖固件实现,可能过时、不完整或错误 |
| 信息焦点 | CPU的硬件特征和能力 | 固件视角下的资产和配置信息 |
- 源码
cat/proc/cpuinfo# 部分输出如下:processor:15vendor_id:GenuineIntel cpu family:6model:165model name:Intel(R)Core(TM)i7-10700 CPU @2.90GHz stepping:5microcode:0x100 cpu MHz:4101.177cache size:16384KB physicalid:0siblings:16coreid:7cpu cores:8apicid:15initial apicid:15fpu:yesfpu_exception:yescpuid level:22wp:yesflags:fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts vnmi pku ospke md_clear flush_l1d arch_capabilities vmx flags:vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec bugs:spectre_v1 spectre_v2 spec_store_bypass swapgs itlb_multihit srbds mmio_stale_data retbleed eibrs_pbrsb gds bhi its bogomips:5799.77clflush size:64cache_alignment:64address sizes:39bits physical,48bits virtual power management:最关键的文件通常位于 arch/<架构>/kernel/ 目录下,因为CPU信息的获取是高度与架构相关的。
对于最常见的 x86/x86-64 架构,主要源码文件是:
arch/x86/kernel/cpu/proc.c
这个文件包含了show_cpuinfo()函数,它是 /proc/cpuinfo 的主要输出函数,负责组织所有信息的显示格式。
我们看到的 processor, vendor_id, model name, flags 等字段的文本输出,都由此函数控制。
但 proc.c 只是信息的“组装车间”。原始数据的获取分散在其他文件中:
CPU识别与数据收集:
arch/x86/kernel/cpu/common.c,cpu.h等文件包含了CPU初始化和识别逻辑。
vendor 字符串(如 “GenuineIntel”)、family、model、stepping 等核心ID,通常通过 cpuid.c 中的代码执行 CPUID 指令获取,并存储在 struct cpuinfo_x86 这个全局数据结构中。
/proc 文件系统注册:
- 创建 /proc/cpuinfo 这个虚拟文件的代码,通常在 fs/proc/cpuinfo.c。它负责将内核的 show_cpuinfo() 函数与用户对该文件的读取操作关联起来。
在线浏览与搜索:
- Elixir Cross Referencer:https://elixir.bootlin.com/linux/latest/source
这是浏览内核源码的绝佳工具,支持交叉引用、语法高亮,并且总是同步最新稳定版内核。
在该网站中查询的部分内容如下:
# https://elixir.bootlin.com/linux/v6.17.12/source/arch/x86/kernel/cpu/proc.c/* * Get CPU informationforuse by the procfs. */ static void show_cpuinfo_core(struct seq_file *m, struct cpuinfo_x86 *c, unsigned int cpu){#ifdef CONFIG_SMPseq_printf(m,"physical id\t: %d\n", c->topo.pkg_id);seq_printf(m,"siblings\t: %d\n", cpumask_weight(topology_core_cpumask(cpu)));seq_printf(m,"core id\t\t: %d\n", c->topo.core_id);seq_printf(m,"cpu cores\t: %d\n", c->booted_cores);seq_printf(m,"apicid\t\t: %d\n", c->topo.apicid);seq_printf(m,"initial apicid\t: %d\n", c->topo.initial_apicid);#endif}三、sysfs cpu
/sys/devices/system/cpu/cpu0/ 目录主要包含CPU的动态运行状态、拓扑关系和可调参数,而不是像dmidecode那样的静态硬件标识信息。
你可以把它理解为CPU的 “实时监控仪表盘和控制系统”,而不是“产品规格说明书”。
下表列出了该目录下的关键文件及其作用:
| 文件/目录名 | 主要作用 | 典型内容/示例 |
|---|---|---|
| cpuinfo_max_freq | 硬件支持的最高频率 | 3500000 (表示3.5 GHz) |
| scaling_cur_freq | 当前运行的实际频率 (动态变化) | 1200000 (表示1.2 GHz) |
| topology/ 子目录 | CPU拓扑信息 (核心、线程关系) | physical_package_id (物理CPU插槽ID)、core_id (核心ID) |
| cache/ 子目录 | 各级缓存信息 (大小、类型、共享关系) | index0/ (L1d)、index1/ (L1i)、index2/ (L2)、index3/ (L3) |
| cpuidle/ 子目录 | 空闲状态(C-State)信息 | 各种深度睡眠状态及其延迟、功耗 |
| thermal_throttle/ | 过热降频事件计数 | 记录因温度过高导致性能下降的次数 |
| online | 在线状态控制 (可写) | 1 (在线) 或 0 (离线),可用来关闭核心 |
四、dmidecode
dmidecode 的源代码可以获取,其核心原理是解析而非探测硬件。为了方便你了解,我整理了几个主要的源码获取渠道:
官方 Git 仓库
- 地址:https://git.savannah.gnu.org/git/dmidecode.git
说明:这是最权威的源代码库,可以获取开发中的最新版本。
1. 核心原理与实现机制
理解 dmidecode 的关键在于其设计哲学和工作方式:
数据来源:dmidecode 不主动扫描物理硬件,它的全部工作是基于 SMBIOS(系统管理BIOS)或 DMI(桌面管理接口)标准,读取并解码由系统BIOS/UEFI固件在开机时收集并存储在内存中的一张硬件信息表。
数据访问方式:程序通过访问 /dev/mem (物理内存设备文件)或 /sys/firmware/dmi/tables/DMI (内核提供的sysfs接口)来读取这些预先生成的表数据。
2. 核心工作流程:
定位入口点:在内存中搜索 SMBIOS 的“入口点”结构,以找到数据表的起始位置。
遍历与解码:读取表头,获得数据类型、句柄和长度。然后,根据数十种预定义的数据类型(如Type 4是处理器信息,Type 17是内存设备信息),调用对应的解码函数将二进制数据转换为可读文本。
输出:将解码后的信息格式化输出到终端。
下面,我们来拆解图中每个关键步骤:
Step 1 & 2: 定位与查找
程序首先在内存固定地址(如 0xF0000)或通过 EFI 系统表,搜索SM或DMI锚定字符串,找到入口点结构。该结构包含了主数据表的地址和长度。随后,dmidecode 开始线性遍历这张表,寻找类型号为 4 的结构。Step 3: 按图索骥,解析核心字段
这是解码的核心。dmidecode 知道 Type 4 结构有一个固定格式的头部和一系列字段。它会按照 SMBIOS 规范中 Type 4 结构的定义,像查字典一样,逐个字节地解析。以下是一些关键字段的解析逻辑:
字段偏移(示例) 字段名 解码逻辑与输出示例- 0x04 处理器厂商 查预定义表。如 0x01 对应 “GenuineIntel”,0x02 对应 “AuthenticAMD”。
- 0x05 处理器类型 查表。如 0x03 (中央处理器) 会输出 “Central Processor”。
- 0x14 - 0x15 核心数 直接读取2字节整数。你之前看到的 Core Count: 8 就来源于此。
- 0x1A - 0x1B 线程数 直接读取2字节整数。得到 Thread Count: 16。
- 0x26 起 特性标志 按位解析一个8字节的位图。例如,判断第2位(从0开始)是否为1,若是则输出 “64-bit capable”。
Step 4: 处理字符串表
Type 4 结构末尾附有一个字符串表,用于存储可变信息(如制造商名称、版本等)。结构体中的相关字段存储的是指向这个字符串表的索引号。例如,Socket Designation(插槽标识)字段的值是 1,dmidecode 就会跳到字符串表,读取第1个字符串(可能是 “CPU Socket #0”)并输出。Step 5: 格式化输出
将所有解析出的字段,按照我们熟悉的 字段名: 值 的格式组织起来,最终呈现在终端上。
3. 关键原理与局限
理解这个过程,就能明白 dmidecode 的能力和局限都源于同一个事实:
它解析的是“元数据”,而非直接探测硬件:数据完全由 BIOS/UEFI 固件在开机自检时填写并存入内存。因此,信息的准确性完全依赖于主板/系统制造商。如果制造商填写了错误、过时或留白的信息(常见于一些虚拟机或廉价硬件),dmidecode 的输出就会不准确。它反映的是“固件认为的硬件是什么”,而不一定是“硬件当前实际是什么”。
数据是静态的:它读取的是开机瞬间的快照,无法反映动态信息,如实时频率、温度或当前电压。获取这些动态数据需要直接通过 /proc/cpuinfo 或 sysfs (/sys/devices/system/cpu/) 接口与内核交互。
为什么需要root权限:历史上,dmidecode 通过直接读取 /dev/mem(整个物理内存)来获取数据,这需要最高权限。现代系统大多通过更安全的 /sys/firmware/dmi/tables 接口提供数据,但该命令仍保持需要 root 权限的传统。
- 源代码