我发现了 lscpu 在旧版和新版的不同输出行为,为了验证我的猜想,我重新编译了 util-linux,这篇文章是对整个过程的记录

我近期开始学习 RHCA 课程,首门课程是 RH442。

我平时很少使用 Red Hat 及其衍生发行版,平时更是 Ubuntu 和 Debian 系最为常用。首先在课上发现了 lscpu 的输出与手边的 Ubuntu 22.04 和 Debian 11 大不相同。

这个问题尤其集中在缓存的大小显示上。

例如,在我笔记本的虚拟机中,AlmaLinux 8 显示的内容如下

 1$ lscpu
 2Architecture:        x86_64
 3CPU op-mode(s):      32-bit, 64-bit
 4Byte Order:          Little Endian
 5CPU(s):              2
 6On-line CPU(s) list: 0,1
 7Thread(s) per core:  1
 8Core(s) per socket:  1
 9Socket(s):           2
10NUMA node(s):        1
11Vendor ID:           GenuineIntel
12CPU family:          6
13Model:               158
14Model name:          Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
15Stepping:            10
16CPU MHz:             2208.000
17BogoMIPS:            4416.00
18Hypervisor vendor:   VMware
19Virtualization type: full
20L1d cache:           32K
21L1i cache:           32K
22L2 cache:            256K
23L3 cache:            9216K
24NUMA node0 CPU(s):   0,1
25Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat md_clear flush_l1d arch_capabilities

注意此处的 L1 缓存和 L2 缓存为 32K+32K 以及 256K

而同样此电脑上的 Ubuntu 显示如下:

 1$ lscpu
 2Architecture:            x86_64
 3  CPU op-mode(s):        32-bit, 64-bit
 4  Address sizes:         45 bits physical, 48 bits virtual
 5  Byte Order:            Little Endian
 6CPU(s):                  2
 7  On-line CPU(s) list:   0,1
 8Vendor ID:               GenuineIntel
 9  Model name:            Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
10    CPU family:          6
11    Model:               158
12    Thread(s) per core:  1
13    Core(s) per socket:  1
14    Socket(s):           2
15    Stepping:            10
16    BogoMIPS:            4416.00
17    Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_t
18                         sc cpuid tsc_known_freq pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault inv
19                         pcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat md_clear flush_l1d a
20                         rch_capabilities
21Virtualization features: 
22  Virtualization:        VT-x
23  Hypervisor vendor:     VMware
24  Virtualization type:   full
25Caches (sum of all):     
26  L1d:                   64 KiB (2 instances)
27  L1i:                   64 KiB (2 instances)
28  L2:                    512 KiB (2 instances)
29  L3:                    18 MiB (2 instances)
30NUMA:                    
31  NUMA node(s):          1
32  NUMA node0 CPU(s):     0,1
33Vulnerabilities:         
34  Itlb multihit:         KVM: Mitigation: VMX disabled
35  L1tf:                  Mitigation; PTE Inversion; VMX flush not necessary, SMT disabled
36  Mds:                   Mitigation; Clear CPU buffers; SMT Host state unknown
37  Meltdown:              Mitigation; PTI
38  Mmio stale data:       Mitigation; Clear CPU buffers; SMT Host state unknown
39  Spec store bypass:     Mitigation; Speculative Store Bypass disabled via prctl and seccomp
40  Spectre v1:            Mitigation; usercopy/swapgs barriers and __user pointer sanitization
41  Spectre v2:            Mitigation; Retpolines, IBPB conditional, IBRS_FW, STIBP disabled, RSB filling
42  Srbds:                 Unknown: Dependent on hypervisor status
43  Tsx async abort:       Not affected

可以看到,不但显示的内容多了许多,并且注意此处的 Cache 值,他是所有的核心缓存加在一起的值。

更加让人迷惑的,是 Debian 11 附带的 lscpu,他的显示效果如下。

 1$ lscpu
 2Architecture:        x86_64
 3CPU op-mode(s):      32-bit, 64-bit
 4Byte Order:          Little Endian
 5CPU(s):              2
 6On-line CPU(s) list: 0,1
 7Thread(s) per core:  1
 8Core(s) per socket:  1
 9Socket(s):           2
10NUMA node(s):        1
11Vendor ID:           GenuineIntel
12CPU family:          6
13Model:               158
14Model name:          Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
15Stepping:            10
16CPU MHz:             2208.000
17BogoMIPS:            4416.00
18Hypervisor vendor:   VMware
19Virtualization type: full
20L1d cache:           32K
21L1i cache:           32K
22L2 cache:            256K
23L3 cache:            9216K
24NUMA node0 CPU(s):   0,1
25Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat md_clear flush_l1d arch_capabilities

此时表面上看起来,他和 AlmaLinux 中的结果差距不大,但此时的缓存是以所有缓存量相加在一起计算的,且不像 Ubuntu 22.04,他并没有注明是多个 instences 的值。这就更令人迷惑了。

我一开始认为,既然两种发行版的 lscpu 都来源于上游的 util-linux 包,他们不应该有这么大的差别,马上想到的是 Red Hat 这个 backport 狂魔用补丁改了行为。

因此我去翻看了来源 CentOS Stream 8 的 RPM 源码树,尽管确实有些与 lscpu 相关的 patch,但并不存在修改输出这种 patch。

从 util-linux 的编译选项中也看不出什么会影响输出的区别。

因此,只能猜测是由于版本不同,上游的包发生了 break change。

并且,三个系统的 util-linux 包版本确实各不相同:

1# AlmaLinux 8
2lscpu from util-linux 2.32.1
3# Debian 11
4lscpu from util-linux 2.36.1
5# Ubuntu 22.04
6lscpu from util-linux 2.37.2

为了验证猜测,我决定干脆来试试。

我选择只编译 2.32.1 和 2.36.1 两个版本。

util-linux 可以从 kernel.org 下载到上游打包好的源码包。

从网站上下载到 util-linux-2.32.1.tar.gzutil-linux-2.36.1.tar.xz 两个源码包并解压。

为了控制变量,我选择了在 AlmaLinux 环境下,使用 AlmaLinux RPM 包中的 configure 选项编译,并且不改变 CFLAGS 的值。

首先安装编译依赖。

由于编译依赖有许多,为了不影响我的环境,我选择使用一个 Docker 容器来编译。

1docker run -it -v `pwd`:/usr/src almalinux:8 bash
2
3# 进入容器内
4cd /usr/src

在安装依赖前,其中两个包需要启用 PowerTools 源

1dnf config-manager --set-enabled powertools

安装编译依赖,这些依赖都是从 SRPM 的 SPEC 文件中得来的。

1dnf install audit-libs-devel gettext-devel libselinux-devel ncurses-devel pam-devel zlib-devel popt-devel libutempter-devel systemd-devel systemd libuser-devel libcap-ng-devel python3-devel gcc automake autoconf libtool bison git-core make diffutils

接下来完成编译

 1cd util-linux-2.32.1
 2./autogen.sh
 3
 4./configure \
 5	--disable-assert \
 6	--with-systemdsystemunitdir=/usr/lib/systemd/system \
 7	--disable-silent-rules \
 8	--disable-bfs \
 9	--disable-pg \
10	--enable-chfn-chsh \
11	--enable-usrdir-path \
12	--enable-write \
13	--enable-raw \
14	--with-python=3.6 \
15	--with-systemd \
16	--with-udev \
17	--with-selinux \
18	--with-audit \
19	--with-utempter \
20	--disable-makeinstall-chown
21
22make

2.36 版也使用相同方式编译。

编译之后,就可以在源码根目录得到我们所需要的 lscpu 二进制文件。在 AlmaLinux 环境中运行这两个版本的文件,得到了确实不同的结果。

 1$ cat /etc/redhat-release
 2AlmaLinux release 8.6 (Sky Tiger)
 3$ ./util-linux-2.32.1/lscpu --version
 4lt-lscpu from util-linux 2.32.1
 5$ ./util-linux-2.32.1/lscpu
 6Architecture:        x86_64
 7CPU op-mode(s):      32-bit, 64-bit
 8Byte Order:          Little Endian
 9CPU(s):              2
10On-line CPU(s) list: 0,1
11Thread(s) per core:  1
12Core(s) per socket:  1
13Socket(s):           2
14NUMA node(s):        1
15Vendor ID:           GenuineIntel
16CPU family:          6
17Model:               158
18Model name:          Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
19Stepping:            10
20CPU MHz:             2208.000
21BogoMIPS:            4416.00
22Hypervisor vendor:   VMware
23Virtualization type: full
24L1d cache:           32K
25L1i cache:           32K
26L2 cache:            256K
27L3 cache:            9216K
28NUMA node0 CPU(s):   0,1
29Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat md_clear flush_l1d arch_capabilities
30$ ./util-linux-2.36.1/lscpu --version
31lscpu from util-linux 2.36.1
32$ ./util-linux-2.36.1/lscpu
33Architecture:                    x86_64
34CPU op-mode(s):                  32-bit, 64-bit
35Byte Order:                      Little Endian
36Address sizes:                   45 bits physical, 48 bits virtual
37CPU(s):                          2
38On-line CPU(s) list:             0,1
39Thread(s) per core:              1
40Core(s) per socket:              1
41Socket(s):                       2
42NUMA node(s):                    1
43Vendor ID:                       GenuineIntel
44CPU family:                      6
45Model:                           158
46Model name:                      Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
47Stepping:                        10
48CPU MHz:                         2208.000
49BogoMIPS:                        4416.00
50Hypervisor vendor:               VMware
51Virtualization type:             full
52L1d cache:                       64 KiB
53L1i cache:                       64 KiB
54L2 cache:                        512 KiB
55L3 cache:                        18 MiB
56NUMA node0 CPU(s):               0,1
57Vulnerability Itlb multihit:     KVM: Mitigation: VMX unsupported
58Vulnerability L1tf:              Mitigation; PTE Inversion
59Vulnerability Mds:               Mitigation; Clear CPU buffers; SMT Host state unknown
60Vulnerability Meltdown:          Mitigation; PTI
61Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl and seccomp
62Vulnerability Spectre v1:        Mitigation; usercopy/swapgs barriers and __user pointer sanitization
63Vulnerability Spectre v2:        Mitigation; Retpolines, IBPB conditional, IBRS_FW, STIBP disabled, RSB filling
64Vulnerability Srbds:             Unknown: Dependent on hypervisor status
65Vulnerability Tsx async abort:   Not affected
66Flags:                           fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid s
67                                 se4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflus
68                                 hopt xsaveopt xsavec xgetbv1 xsaves arat md_clear flush_l1d arch_capabilities

这样一来,基本可以确定,这个不同的行为就是 util-linux 升级版本产生的 break change.

在翻 RPM 的时候我发现 RHEL 9 的 util-linux 的上游源码使用的是 2.37.2 版本,即与 Ubuntu 22.04 使用的版本一致,因此以后的 RHEL 也许也将会看到类似的结果了。