如果你做过Linux服务器的性能评估,一定对fio、iperf3、unixbench这些名字不陌生,但真正要把一台机器的“底子”摸清楚时,我总会翻出上世纪九十年代出生的lmbench3。这套微基准测试套件虽然年纪大,却能一次性覆盖内存带宽、内存延迟、上下文切换、进程创建、文件系统操作、TCP/UDP延迟等几十个维度,而且体积小、部署快,特别适合做系统级横向对比。不过,它毕竟是一套老代码,在现代Linux发行版上安装、使用时会遇到不少坑,网上资料又比较零碎。这篇文章就结合我自己的实测经验,把lmbench3的安装、使用、常见问题和结果解读一次性说清楚,适合运维、性能测试工程师以及想深入了解自己机器底层能力的开发同学。
1. lmbench3是什么,为什么现在还在用它
1.1 它到底能测什么
lmbench3是一套可移植的微基准测试工具集,最早由Sun Microsystems的Carl Staelin等人开发,目标是对系统硬件、操作系统以及应用程序的基础能力做细粒度测量。它测的核心项目可以分成两大类:带宽(Bandwidth)和延迟(Latency)。
带宽类测试包括内存拷贝带宽(bw_mem)、管道带宽(bw_pipe)、TCP带宽(bw_tcp)、UDP带宽(bw_udp)、Unix socket带宽、文件读取带宽等。延迟类测试更有意思,包括内存读延迟(lat_mem_rd)、上下文切换耗时(lat_ctx)、进程创建与退出耗时(lat_proc)、系统调用耗时(lat_syscall)、信号处理开销、文件系统创建与删除延迟(lat_fs)、TCP连接建立延迟、UDP往返延迟、页面缺失处理耗时等。
我最早用lmbench3,是想对比两台看起来“配置差不多”的服务器,为什么跑业务时差异明显。当时用fio和iperf3分别看磁盘和网络,发现两者都很正常,后来跑了一轮lmbench3,发现其中一台的内存延迟比另一台高了将近一倍,再查下去是BIOS里NUMA节点交错策略没开,导致跨节点访问频繁。这个结论用其他工具很难快速得出来。
1.2 相比现代测试工具,它的独特价值
很多人问,为什么不用perf、strace或者更现代的benchmark工具?这些工具当然有用,但定位不太一样。perf更像是一个“探针”,适合去深入分析某一个具体瓶颈,而对多维度底层基线数据,厂家往往愿意给一个统一可比的分数。lmbench3的优势在于三点:
第一,覆盖维度足够全,一套工具能同时拿到几十个基础性能指标,省得在多个工具之间来回切换。第二,体积小、依赖少,编译出来不到几MB,可以轻松丢到各种老旧服务器或嵌入式环境里跑。第三,可移植性好,它能跑在Linux、BSD、Solaris、AIX甚至一些古老的Unix系统上,横向对比不同操作系统版本时非常方便。
当然,它也有明显短板,比如没有图形界面、结果展示比较原始、部分微基准在现代CPU和内核上测得的值需要小心解读。所以我的建议是:lmbench3适合做系统级基线评估和横向对比,不适合用来指导某一个具体应用的参数调优。真要看业务体感,还是需要配合fio、iperf3、perf这类工具一起用。
2. 安装:从下载到编译通过的正确姿势
2.1 环境准备与下载
lmbench3现在最常见的发行版本是3.0-a9,网上流传的压缩包名称一般是lmbench-3.0-a9.tar.gz。由于原站点年代久远,直接访问不一定稳定,建议去GitHub搜索“lmbench”,找3.0-a9版本的镜像仓库下载。无论从哪下载,解压后目录结构基本一致,核心代码都在src子目录里。
在开始之前,请先确认系统装好了基础编译工具链。Debian/Ubuntu系执行:
sudo apt-get install -y build-essential m4RHEL/CentOS系执行:
sudo yum groupinstall -y "Development Tools" sudo yum install -y m4除了gcc和make,最容易被忽略的是m4。lmbench的构建过程会用到它来生成部分配置头文件,如果系统里没有m4,编译阶段会报一些比较诡异的错误,比如找不到某些头文件,这时候完全想不到是m4缺失导致的。
2.2 编译前的关键配置
进入解压后的目录,切换到src:
cd lmbench-3.0-a9/src make直接执行make时,构建脚本会自动探测当前操作系统并生成对应的配置。在大部分主流发行版上,这一步能顺利通过。如果提示unknown OS或无法识别系统类型,可以手动指定操作系统类型,比如:
make OS=linux这个操作的本质是告诉构建系统去加载src/scripts/Makefile.linux之类的配置文件。不同发行版可能标识不同,通常试linux就能解决。
如果机器上没有root权限,编译本身不影响,但后续跑测试时很多项目会因权限不足而失败,后面会单独讲。建议在有root权限的环境下编译和测试。
2.3 编译过程与踩坑实录
我用的测试机是Ubuntu 22.04,gcc版本12,编译时遇到了一个典型问题:报错implicit declaration of function 'getpagesize'。原因是lmbench3的年代太早,源码里很多头文件没有显式声明_GNU_SOURCE,而新版本glibc把一些接口的隐式声明收紧,导致gcc在编译时直接抛出warning甚至error。
解决办法很简单,在src/Makefile里找到CFLAGS相关行,把默认的-O2改成-O2 -D_GNU_SOURCE,或者用命令行传递:
make CFLAGS="-O2 -D_GNU_SOURCE"我的经验是直接改src/Makefile更省事,因为后续每次保存配置、重新编译都会自动带上这个宏,避免重复传参。如果没改Makefile只靠命令行传参,某些子模块在单独编译时可能会漏掉CFLAGS,又回到报错原样。
另外,在gcc 13以上版本编译时,还可能遇到implicit declaration相关的waring被升级为error的情况。如果不想逐个头文件去补声明,可以在CFLAGS里追加-Wno-implicit-function-declaration,把这类提示降级为警告,让编译继续往下走。这个参数不影响生成的二进制功能,只是用来绕过老代码与现代编译器之间的小摩擦。
编译成功后,src目录下会生成一批带的时间戳的可执行文件,比如bw_mem、lat_mem_rd、lat_ctx、lat_proc、lat_fs、lat_tcp等。在决定跑全量测试之前,建议先单独运行几个小命令验证一下,比如:
./bw_mem 64m rd如果这条命令能正常输出带宽数据,说明编译产物基本可用。
3. 使用:跑一次完整基准并看懂结果
3.1 交互式跑测与自动化跑测
完整跑一遍lmbench3有两种方式:一是在src目录里执行make results,它会连带执行配置、编译、测试、汇总等一整套流程,适合全量评估;二是单独执行src下的各个测试程序,适合只关心某几个指标。
make results这套流程是交互式的,运行期间会问不少问题,比如测试结果保存到哪个目录、文件系统测试的最小目录大小设多少、是否允许在NFS等网络文件系统上测试,等等。第一次跑的时候,大部分人看到一连串提问都会有点懵,我的建议是除了“是否允许网络文件系统测试”按需选择外,其余都直接回车接受默认值。它给出的默认值在绝大多数服务器上都是合理的,后期也完全可以改。
由于全量跑测时间较长,一般在几十分钟到几个小时不等,强烈建议放到tmux或screen会话里执行,防止SSH断开导致工作前功尽弃。我第一次跑的时候没注意,直接挂在SSH终端里,中间网络闪断一次,整个测试过程全部白费,后来学乖了,所有耗时任务一律先进tmux。
如果想自动化跑测,可以把交互输入通过管道喂进去。比如:
yes "" | make results这个命令会把默认回答一路回车下去。如果你的场景需要自定义某几个参数,可以分步执行:先make config生成配置文件,再修改results里的配置文件,最后再执行make results。这样比纯交互式可控性高很多。
3.2 核心测试项速查与常用参数
单独执行某个测试程序时,先cd src,然后运行对应命令。我整理了一套日常最常用的组合:
| 测试目标 | 命令示例 | 说明 |
|---|---|---|
| 内存读延迟 | ./lat_mem_rd -P 1 128M 512 | 测128MB数组在不同步长下的读取延迟 |
| 内存拷贝带宽 | ./bw_mem -P 1 128M cp | 测128MB内存拷贝带宽 |
| 内存读写带宽 | ./bw_mem -P 1 128M rd | 测128MB内存读带宽 |
| 上下文切换 | ./lat_ctx -P 1 -s 64 2 4 8 16 24 32 64 96 128 | 测不同进程数量下的上下文切换延迟 |
| 进程创建延迟 | ./lat_proc fork | 测fork+wait流程耗时 |
| 进程创建+执行延迟 | ./lat_proc exec | 测fork+exec+wait流程耗时 |
| 文件创建删除延迟 | ./lat_fs /tmp | 在指定目录测文件创建和删除耗时 |
| TCP连接延迟 | ./lat_tcp -P 1 127.0.0.1 | 测本机TCP连接建立耗时 |
| UDP延迟 | ./lat_udp -P 1 127.0.0.1 | 测本机UDP通信延迟 |
| 系统调用延迟 | ./lat_syscall open | 测open系统调用耗时 |
这里重点说两个高频使用的参数。-P表示并行进程数,如果只看单线程性能,建议固定为1;-N表示测试重复次数,一般默认足够。对lat_mem_rd来说,第一个数字是数组大小,第二个数字是最大步长。数组大小要明显大于内存总量的一半,才能保证访问真正落到内存而不是被缓存兜住;步长则是为了模拟随机访问模式,通常取128到512之间即可。
跑完以后,屏幕会直接输出结果,比如lat_mem_rd的输出会是一个二维序列,左边是数组大小,右边是对应的延迟时间。如果只想做快速对比,可以直接把这些数字记下来,但更标准的做法是看汇总文件。
3.3 结果文件怎么读、怎么对比
执行make results后,结果会被写入results目录下的子目录中,子目录名通常与机器名和系统标识相关。每个子目录里包含多个单独结果文件,以及一个汇总文件。你可以用make see直接查看汇总,也可以进到对应目录下cat汇总文件。
汇总文件里的核心内容分为两块:一块是机器基本信息,包括CPU频率、操作系统版本、编译参数等;另一块是各项基准测试的最终数值。对比两台机器时,我的习惯是并排打开两份汇总文件,先看机器基本信息确认两者是否处于同等的配置状态,比如CPU是否都锁定在性能模式、内存是否都开启了同样的NUMA策略,然后再看指标数据。
这里有一个很关键的经验:不要只对比平均分,要看单指标分布。例如lat_mem_rd的输出里,数组大小从1KB增长到128MB的过程中,延迟会呈现阶梯式上升。每一段“台阶”分别对应L1 Cache、L2 Cache、L3 Cache和内存。如果你发现两台服务器的L3 Cache延迟有明显差异,那问题很可能出在CPU型号或固件配置上,而不是操作系统层面。
4. 常见问题与排查技巧实录
4.1 编译阶段典型报错速查表
我整理了在编译lmbench3时最常碰到的几类报错和解决方案:
| 报错信息 | 原因分析 | 解决办法 |
|---|---|---|
implicit declaration of function 'getpagesize' | 老代码缺少_GNU_SOURCE声明,新glibc收紧隐式声明 | CFLAGS加-D_GNU_SOURCE |
各种implicit declaration被当error | gcc新版默认将隐式声明视为错误 | CFLAGS加-Wno-implicit-function-declaration |
m4: command not found | 缺少m4宏处理器 | 安装m4 |
unknown OS或系统类型无法识别 | 构建脚本无法自动匹配当前系统 | 手动指定make OS=linux |
| 提示权限不足无法编译/运行 | 部分场景需要写系统目录 | 使用root或编译安装到用户目录 |
有朋友问过,能不能不做任何修改、直接编译安装?在较老的操作系统上确实可以,但在2024年以后发布的Linux发行版上,完全不改动就一次性通过的几率比较低。不要怕改CFLAGS,这属于正常操作。
4.2 运行阶段权限、容器、网络干扰
编译通过后,运行阶段还会遇到一些问题。最常见的是权限类报错。lmbench的部分测试会尝试调整进程优先级、设置实时调度策略,这些操作需要root权限。如果当前用户不是root,运行时会提示无法设置调度器或类似信息。我的建议是统一加sudo运行,尤其是make results这种全量测试,否则某些项目会静默跳过或得到错误数据。
在容器环境中,这个问题会更明显。Docker容器默认对进程的能力做了裁剪,即使容器内root用户,也可能没有CAP_SYS_NICE之类的权限,导致无法完成实时调度相关测试。解决办法是启动容器时加上--privileged参数,或者至少加上--cap-add=SYS_NICE。但我个人观点是,如果你要做严谨的CPU性能测试,尽量不要在容器里跑,虚拟化层和共享内核会引入额外干扰,测试出来的数字跟宿主机直接跑会有偏差。容器适合验证工具流程是否通顺,不适合用来出正式数据。
网络相关测试也容易踩坑。比如测lat_tcp、bw_tcp时,如果机器上开着防火墙,延迟和带宽数字都会变得非常难看。解决办法是先把防火墙策略放行对应端口,或者使用回环地址测试本机协议栈性能。还有一点,云主机如果开启了安全组或流量限速策略,默认的测试数据往往不是实例性能的真实反映。要区分本机协议栈和网络链路两件事,最好的办法是先测127.0.0.1,再测另一个内网IP,两者对比就能大致判断干扰来自哪个环节。
4.3 结果异常怎么排查
有时候测试结果跟预期相差很大,不要急着怀疑机器坏了,先用下面几步排查。
第一,检查CPU频率是否稳定。现代处理器都有动态调频机制,如果CPU governor处于powersave模式,算出来的内存带宽和延迟数据会严重偏低。测试前建议把cpu调到performance模式:
cpupower frequency-set -g performance或者手动将/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor设置为performance。这一步做完,数据波动通常会明显下降。
第二,检查后台负载。跑测时如果有其他业务进程在抢占CPU、内存或磁盘IO,结果自然会乱。测试前至少保证系统空闲,关闭不必要的服务,尤其要停掉cron定时任务和数据备份任务。我遇到过不止一次,白天跑lmbench3数据忽高忽低,晚上重跑就稳定了,最后发现是监控agent定时采样导致的。
第三,关注NUMA影响。对多路服务器,尤其是两路以上的机器,内存访问可能跨NUMA节点。如果lmbench的任务被调度到node 0,但内存分配落在node 1,延迟数据会高出一截。排查方法是用numactl --hardware查看节点拓扑,再用numactl --cpunodebind=0 --membind=0锁定测试进程的内存亲和性。这样测出来的数据才具备可比性。
第四,重复测试取中位数。微基准测试天然存在一定抖动,一次跑出来的数据不具备说服力。我的习惯是对关键指标至少跑三遍,取中位数作为最终结论。而且不要只看平均值,异常值往往能暴露问题,比如某一次延迟突然飙高,通常意味着测试过程中发生了调度抢占或中断干扰。
5. 一点实操心得
跑lmbench3这件事,看起来简单,但想拿到的是一份“能打到报告里、经得起推敲”的数据,需要对测试环境有很强的控制力。我自己每次跑之前都会固定一套流程:先查CPU governor,再查NUMA策略,关掉不必要的后台任务,最后放在tmux里跑。没有这套固定动作,结果是很难复现的。
另外说一个容易被忽略的细节:lmbench3的默认结果汇总里,会有calibration相关数据,这是工具自动校准计时器时产生的,不用管它,但如果在结果文件里看到某个测试项耗时过短,比如小于1微秒,要警惕它是否真的有效。这类微基准数据对时钟精度很敏感,最好先跑一遍make results里的校准流程,确认基准值正常再开始正式测试。
最后再分享一个小技巧:如果你只想快速比较两台机器,不用傻傻跑完所有项目。先分别跑./bw_mem 128M cp、./lat_mem_rd -P 1 128M 512、./lat_ctx -P 1 -s 64 2 4 8 16 24 32 64这三条,基本上CPU缓存、内存带宽、内存延迟和调度器能力都能反映出来。其余指标按需补充就行。这样一轮测下来,10分钟之内就能拿到关键基线数据,效率高很多。