简介:Linux系统压力测试工具stress的完整源码与文档包,面向系统管理员、运维工程师及内核开发者,用于评估服务器在CPU密集、内存分配等负载下的稳定性与极限性能。压缩包共含32个文件,大小仅199KB,以configure、Makefile.am、Makefile.in等构建脚本为主,辅以stress.c源码、texinfo文档、README说明及测试校验脚本,便于使用者阅读源码、自行编译或交叉验证。目前已有1036人学习。资源内附详细使用说明与参数释义,涵盖--cpu、--vm、--vm-bytes等常用选项,可快速生成定制化压力场景;同时提供configure.in、aclocal.m4等自动构建辅助文件,方便在主流发行版上安装部署。通过结合top、vmstat等监控工具,读者能系统掌握压力测试方法,并据此优化系统资源配置,提升高负载环境下的可靠性。
1. stress工具:给Linux服务器施加一次可控的“人工高负载”
一台新上线的服务器,配置看着很豪华,但业务方反馈“一到大促就卡”。查了CPU平均使用率、内存占用、磁盘IO,全都在阈值以下,你拿不出一个结论:这台机器到底能不能扛住突发压力?这就是我用stress的理由:它能在几分钟内模拟出CPU满载、内存吃紧、IO排队、磁盘持续写入等各种负载,把服务器的真实承受边界试出来。它不是一个诊断工具,而是一个加压源——配合监控命令使用,能把“我觉得没问题”变成“数据证明没问题”。适合所有做系统运维、性能压测和服务器选型评估的开发者。
2. 装好stress的第一天:用最小命令跑通CPU、内存、IO、磁盘四种压测
2.1 安装:Debian系和RedHat系各一条命令
stress是个非常轻量的小工具,主流Linux发行版基本都把它收进了官方软件源,不需要额外配置第三方仓库。我在测试机上验证完安装后,才会考虑在目标机器上安装,避免在生产环境临时装包引入不确定性。
# Debian / Ubuntu 系 sudo apt update sudo apt install -y stress # CentOS / RHEL / Fedora 系 sudo yum install -y stress # 新版 Fedora / RHEL 也可以直接 sudo dnf install -y stress装完先跑一下stress --version,能输出二进制版本号说明工具可用。如果系统源里没有这个包(精简版发行版偶尔会遇到),就要走编译安装。编译安装也简单,先把编译工具链装好,再解压源码包依次执行configure、make、make install:
sudo apt install -y build-essential tar -xzf stress-*.tar.gz cd stress-*/ ./configure make sudo make install源码包从软件源或项目发布页获取,stress-*按实际解压出的目录名替换。编译完成后同样用stress --version做验证。这里有一个经验:编译安装时尽量把源码解压到 /usr/local/src 这类系统目录,不要放在用户家目录下,避免权限混乱。
2.2 第一条压测命令:让CPU满载30秒
装好之后第一件事是确认它能产生真实负载。我用一条最小命令验证:生成1个CPU worker,持续运行30秒。
stress --cpu 1 --timeout 30--cpu 1表示生成1个worker进程,这个进程内部会不断对一个随机数做平方根运算,从而占满一个逻辑核心;--timeout 30表示运行30秒后自动终止。如果机器只有1个核心,这条命令足以让整个系统几乎满载;如果是多核机器,从1开始观察负载曲线更稳妥。
正常跑完后,stress会打印一行successful run completed in 30s。这个输出说明工具本身工作正常,并且超时控制生效了。如果中途想停止压测,按 Ctrl+C 发送中断信号,stress会杀掉它管理的所有worker进程并退出,但已经产生的临时文件不会自动清理。
接下来试一下多核服务器上的常见用法:
stress --cpu 4 --timeout 60在4核心机器上,这条命令会让4个worker各占一个核心,CPU总使用率接近100%。在8核心机器上跑--cpu 4,只有4个核心满载,另外4个核心保持空闲。理解这一点很重要:压测前先nproc确认核心数,再决定--cpu取多少。我的经验值是:等于核心数做满载测试,一半核心数做半载测试,超过核心数做超载测试。满载验证稳定性,半载验证性能余量,超载对系统冲击大,不适合在生产机上跑。
2.3 用uptime和mpstat确认压力真的生效
stress自己打印的日志只能说明它启动成功了,不能说明系统真的进入了高负载状态。压测过程中我会另开一个终端,用几条命令交叉验证:
uptime mpstat -P ALL 1 free -h vmstat 1 3uptime主要看load average的第一个值,压测期间它会快速抬升。mpstat按1秒频率打印每个逻辑核心的使用率,正在跑CPU worker的核心%usr会接近100。free -h用于验证内存类压测,--vm-bytes设了多大,used就应该增加多少。vmstat 1 3同时能看到运行队列r、CPU user%、系统调用sy、IO等待wa几个关键指标。
只看stress自己的输出会出现一个盲区:worker启动了,但可能因为CPU配额、cgroup限制或者虚拟化抢占,实际分配到的时间片很少,压力根本没顶上去。所以我会再用pidstat确认单个worker的CPU占用:
pidstat -p $(pgrep -f 'stress --cpu' | head -1) 1如果某个worker的%CPU接近100,说明它确实在满负荷运转;如果只有30%、50%,说明系统层面已经把它限住了,这台机器的真实负载能力需要重新评估。
3. stress压力参数拆解:CPU、内存、IO、磁盘与混合模式
3.1 CPU压测:--cpu N与系统核心数之间的关系
stress的CPU worker实现很朴素:每个worker进程在死循环里执行平方根运算,持续占用一个逻辑核心的整数和浮点运算单元。对于散热验证来说,3到5分钟持续满载就能让温度传感器读数明显上升;对于稳定性验证,一般至少跑30分钟以上。
选--cpu N时,先nproc看逻辑核心数。核心数以内最理想,比如4核机器--cpu 4,每个worker各占一个核心,互不争抢。超过核心数时,比如2核机器跑--cpu 4,4个worker会抢2个核心,load average会显得很高,但每个worker分到的时间片减半,整体吞吐量不升反降。我在给某模拟项目X做基准测试时,用--cpu 8在4核机器上跑,得到的性能数据完全没有参考价值,后来才发现核心数对不上。
--backoff是一个容易被忽略但很实用的参数,单位是微秒。它让每个worker在启动前先睡指定时间,分批进入满负荷状态,避免所有worker同一瞬间拉起造成负载尖峰。
stress --cpu 4 --backoff 100000 --timeout 60这里100000微秒等于0.1秒,4个worker会按顺序错开启动。load average仍然会涨,但启动瞬间的“冲顶”平滑很多,适合在继续承载业务流量的机器上做温和压测。
3.2 内存压测:--vm、--vm-bytes、--vm-keep的搭配
内存压测是stress最容易把系统搞死的一个维度。原理是每个vm worker循环执行malloc分配内存、按步长touch每个页面、再free释放的流程。这里的touch很关键:Linux默认是延迟分配物理内存的,malloc成功只能说明虚拟内存够用,必须真正写入每个页面,物理内存才会被消耗掉。
默认情况下,每个vm worker分配的物理内存是256MB。一个完整用法是:
stress --vm 2 --vm-bytes 1G --vm-stride 64 --vm-keep --timeout 602个内存worker各占1GB,共2GB物理内存被持续占住。--vm-stride 64表示每64字节touch一次,保证每个4KB页面都被访问到;--vm-keep表示touch完内存后不释放,而是反复重新touch同一块区域。不加--vm-keep时,worker会在分配和释放之间高频切换,内存使用率波动很大,不容易观察稳定的占用量。
计算总内存占用时,要把--vm数和--vm-bytes相乘。2个worker各1GB就是2GB,如果机器本身只有2GB内存,加上系统常驻进程,OOM killer几乎必然被触发。我一般先执行free -h看available值,再让乘积不超过available的一半,给系统自身留足余量。这里没有玄学,纯粹是给内核留活路。
3.3 IO压测与磁盘压测:--io和--hdd的作用范围不一样
很多人以为stress --io N就是模拟磁盘IO压力,实际上它调用的是sync()系统调用,让内核把脏页写回磁盘。这个操作会对块设备产生一定压力,但更多是在消耗CPU。在SSD上跑--io,负载特征更接近CPU高占用,而不是纯粹的磁盘吞吐压力。这是一个容易误判的地方。
真正制造磁盘写入压力的是--hdd。每个hdd worker循环执行创建文件、写入数据、关闭、删除这个流程:
stress --hdd 2 --hdd-bytes 4G --hdd-nonzero --timeout 602个磁盘worker各写4GB,默认在当前目录下创建临时文件。不加--hdd-nonzero时写入全零字节,某些文件系统或压缩层会对全零块做特殊优化,导致实际落盘量低于预期;加上这个参数后写入非零数据,测出来的写入量更接近真实场景。跑完后worker会自动清理临时文件,但中途Ctrl+C中断则可能残留,需要手动清理。
--io和--hdd的选择要看目标:怀疑应用卡在块设备读写上,用--hdd制造吞吐压力;怀疑是频繁sync导致CPU升高,用--io复现高占用现象。两条路走的方向不一样。
3.4 混合压测:把多类压力叠加在一起
生产环境里单一瓶颈不常见,更多是CPU、内存、IO同时吃紧。stress允许一条命令指定多类worker:
stress --cpu 2 --vm 1 --vm-bytes 1G --io 1 --hdd 1 --hdd-bytes 2G --timeout 120这条命令同时跑2个CPU worker、1个内存worker、1个IO worker、1个磁盘worker,持续120秒。各类型worker相互独立,stress创建进程组统一管理,任意一个崩溃不会影响其他worker继续跑。
混合压测时load average的预期值不能按“参数个数求和”来算。4核机器上跑--cpu 2 --vm 1 --io 1,IO worker和VM worker本身也会消耗CPU,实测load可能在3.5到4.0之间。正确做法是先跑30秒观察稳定值,再根据数据调整worker数量,而不是拍脑袋设定预期。
4. 让压测数据有说服力的三个步骤:基线、监控、判读
4.1 压测前先记录零负载基线
不做基线直接压测,是很多压测报告被质疑的根本原因。没有压测前的CPU空闲率、内存占用和load average,你没法证明压力是stress造成的,还是系统本来就一直在高负载。在压测前我会先记录一组60秒的基线数据:
uptime > /tmp/baseline_load.txt free -h >> /tmp/baseline_load.txt mpstat -P ALL 1 60 > /tmp/baseline_mpstat.txt vmstat 1 60 > /tmp/baseline_vmstat.txt这些命令连续记录60秒的CPU使用率、负载均值、内存状态,输出保存到 /tmp 下。基线数据里最该关注的是idle百分比和load average——如果idle本来就只有30%,说明系统本来就不空闲,压测结果不能算在stress头上。
基线记录这一步经常被跳过去,尤其是时间紧张的时候。我的做法是把它固化到一个脚本里,每次压测前自动执行,不依赖手工操作。
4.2 压测中持续采样,别只看终点
我犯过的错误是“压完才想起来看数据”,结果只有零点和终点,中间过程全是盲区。现在的做法是压测的同时另开终端持续采集指标,采样频率不用太高,5秒一次足够:
watch -n 5 'uptime; free -h | head -2; mpstat -P ALL 1 1 | tail -5'watch每5秒刷新一次负载和内存信息,mpstat -P ALL 1 1截取当前瞬间的CPU核心采样。多核场景下重点看是不是所有核心都均匀打满,负载是否稳定,有没有出现load先冲高再回落的抖动。稳定的压力表现是:负载在开始10到20秒逐步上升,然后稳定在一个区间不再大幅波动。如果曲线一直在抖,说明有别的定时任务或服务在抢资源。
如果需要归档,用循环采样更好,数据落盘后可以画趋势图:
for i in $(seq 1 60); do echo "$(date +%H:%M:%S) $(uptime)" >> /tmp/load_sample.txt sleep 5 done这个循环跑5分钟,每5秒记一条带时间戳的load记录。压测结束后把采样文件和其他日志一起归档,作为性能验证报告的附件。
4.3 多快的load算正常,多高的CPU算合格
判读压测结果需要预期模型。单核机器上--cpu 1的load约为1.0;4核机器上--cpu 4的load约为4.0,每个核心user%持续接近100。如果负载没达到预期,比如4核机器跑--cpu 4而user%只有60%,原因通常是其他进程在竞争CPU,或者stress进程被cgroup、虚拟化限流。
内存压测的判读标准是物理内存被真实占用。free -h中的available应明显下降,实际占用量和--vm-bytes总量不能差太多。如果malloc了1GB但free显示可用内存没怎么变化,说明页面没有被touch到,检查是不是漏了--vm-stride,或者系统启用了一种特殊的内存overcommit模式。
磁盘压测的判读标准不是看磁盘用了多少,而是看块设备的忙闲状态。用iostat -x 1看%util和await:
| 指标 | 合理范围 | 异常信号 |
|---|---|---|
| %util | 机械盘接近100%,SSD可能较低 | 长期100%且await升高 |
| await | 机械盘10-20ms,SSD低于2ms | 持续增长说明排队严重 |
| w_await | 与设备类型相关 | 明显高于基线说明写路径堵塞 |
stress的--hdd属于纯写入型负载,如果目标是测混合读写,默认方式就不够,需要配合其他工具一起做。判读的本质是拿压测数据和基线数据对比,而不是对比一个想象中的“标准值”。
5. stress压测避坑记录:四个真实翻车现场
5.1 内存Worker开太大,系统直接被OOM
现象:在8GB内存的物理机上执行stress --vm 2 --vm-bytes 4G --timeout 300,运行不到1分钟系统失去响应,SSH连接也断了,重连后通过dmesg确认是OOM killer触发并杀掉了大量进程。
原因:2个worker各占4GB,合计8GB,等于整机内存容量。系统本身还有常驻进程占用内存,内核为了保证自身存活只能启动OOM killer。更关键的是--vm-keep模式下内存不释放,OOM一旦触发就无法自动恢复。
解决:压测前先看free -h的available值,把--vm数和--vm-bytes的乘积控制在available的50%以内。如果确实要做OOM演练,也请在测试机上做,不要拿生产机冒险。这条翻车记录我后来每次讲内存压测都会提,因为代价太高了。
5.2 --hdd把根分区写满
现象:执行stress --hdd 1 --hdd-bytes 50G模拟大文件写入,结果几分钟后系统告警磁盘使用率超过90%,根分区被临时文件写满,服务日志开始报错。
原因:stress的hdd worker默认在当前用户目录创建临时文件,当前目录如果正位于根分区,数据就会直接占用系统盘空间。我当时忘了先执行df -h检查各分区剩余空间。
解决:指定一个专用工作目录,并限制写入总量不超过目标分区可用空间的30%。例如先mkdir -p /var/tmp/stress_test,再在该目录下执行压测命令。压测结束后检查目录下有没有类似stress.xxxx的临时文件,有就手动清理。这个坑的根本教训是:磁盘压测前必须先看磁盘分区,而不是只看总量。
5.3 没设timeout,压测变成无限跑
现象:执行stress --cpu 8后去开会,回来发现系统满载跑了2个多小时,期间业务响应明显变慢,造成了线上影响。
原因:--cpu、--io这类参数不设--timeout时,stress会一直运行到被信号终止。我当时没有注意这一点,也没有用timeout命令做外层兜底。
解决:现在每次跑stress都强制带--timeout,即便是10秒的短压测也带上。有时还会再用timeout 600 stress --cpu 8做双重保险。如果确实需要长时间压测,用nohup加日志输出,并在计划任务里设定自动kill时间:
timeout 3600 nohup stress --cpu 8 --vm 1 --vm-bytes 1G >> /tmp/stress_long.log 2>&1 &这样即使忘记手动清理,一小时后也会自动结束。没有期限的压测等于事故,这个习惯一定要养成。
5.4 压测期间SSH断开,CPU满载不是唯一原因
现象:在云主机上跑stress --cpu 8 --timeout 600,跑了一会儿SSH窗口失去响应,重连很慢,但控制台的CPU监控只显示约70%使用率。
原因:云主机的CPU配额有限,8个worker在争抢受限CPU资源时会触发宿主机的调度限制,SSH进程能分到的CPU时间也被挤占,网络包处理延迟,连接就断了。这不是stress本身的bug,而是云环境资源约束带来的“假死”。
解决:云服务器上做压测前,先查清楚实例规格的CPU配额,是否支持长时间满载。临时验证可以减半--cpu数,或者用taskset把stress绑定到指定核心,给系统保留余量:
taskset -c 0,1 stress --cpu 2 --timeout 300这条命令只让stress在0号和1号核心上跑,其他核心留给系统进程和SSH通信。压测结束后立刻用pkill -f stress清理所有残余进程,避免压测进程残留。
6. 把stress变成压测骨架:一个可复用的自动化压测脚本
6.1 脚本设计:基线、加压、采样、清理一次完成
手工跑stress最大的问题是数据分散:基线在一处,压测输出在一处,采样记录又在另一处,最后归档要拼半天。我用一个bash脚本把链路串起来:
#!/bin/bash # usage: ./stress_run.sh <duration_seconds> <report_name> DURATION=${1:-300} NAME=${2:-report} CPU_CORES=$(nproc) RDIR="/tmp/stress_${NAME}_$(date +%Y%m%d_%H%M%S)" mkdir -p "$RDIR" # 1. 基线 uptime > "$RDIR/baseline.txt" free -h >> "$RDIR/baseline.txt" cat /proc/loadavg >> "$RDIR/baseline.txt" # 2. 启动压测 stress --cpu $((CPU_CORES/2)) --vm 1 --vm-bytes 512M \ --hdd 1 --hdd-bytes 1G --timeout "$DURATION" \ > "$RDIR/stress.log" 2>&1 & SPID=$! # 3. 周期采样 END=$((SECONDS + DURATION)) while [ $SECONDS -lt $END ]; do echo "$(date +%T) $(uptime)" >> "$RDIR/load.txt" sleep 10 done # 4. 等待结束并汇总 wait $SPID echo "done -> $RDIR"脚本逻辑:默认压5分钟,用一半核心数做CPU压测,叠加512MB内存worker和1GB磁盘worker,每10秒记一次load。$SECONDS是bash内置变量,配合$DURATION控制采样总时长。命令最后的&让stress在后台运行,主脚本继续执行采样循环,压测结束后所有数据都在以时间戳命名的目录里,直接打包归档。
实际使用时要改三处:--hdd-bytes根据目标分区剩余空间调整;CPU_CORES/2如果要做全量压测就改成$CPU_CORES;采样间隔从10秒改成1秒能看到更细的负载攀升过程,但日志文件会大很多。脚本里没有做worker残留清理,建议压测结束后跑一次pkill -f stress确认进程都退干净了。
我的习惯是压测结束后再记录一次内核版本和stress版本号,一起存进报告目录。因为不同内核版本对内存分配、IO调度的表现有明显差异,这个存档能解释“为什么这次压测数据和上次不一样”的疑问。stress工具本身很简单,但一套完整的压测流程需要基线、采样、版本记录这些东西兜底,缺一个都会让数据失去说服力。希望这篇能帮你在下次压测时少踩几个坑。
本文还有配套的精品资源,点击获取