运维工程师的 perf 入门实战

用 2 个可运行实验,学会性能分析的"工程思路"

"我怎么用 perf 把问题抓出来"*


一、为什么运维一定要会 perf?

线上慢的时候,常见对话是:

  • 「是不是网络慢?」
  • 「是不是磁盘不行?」
  • 「CPU 不是才 30% 吗?」
  • 「感觉哪里不对,但说不清」

👉 perf 的价值只有一句话:

把"感觉慢",变成"函数级证据"。


实验一:用 perf 看懂 CPU / IO / 网络问题

这是 perf 的地基实验,只解决一件事:

这台机器,到底慢在 CPU、IO 还是网络?


实验一整体设计(你先看懂)

我们用一个 Python 程序,人为制造三类问题

模块 人为制造的问题 perf 里能看到
CPU 纯计算死循环 Python 函数热点
IO 频繁写文件 + fsync write / fsync
网络 socket 收发 tcp_sendmsg / recv

程序结构很简单:

复制代码
main
 ├── cpu_task()        # CPU 热点
 ├── io_task()         # IO 阻塞
 └── network_task()    # 网络 syscall

实验一代码(直接可运行)

保存为 perf_demo.py

python 复制代码
import threading
import socket
import time
import os

FILE_PATH = "/tmp/perf_io_test.log"
SERVER_ADDR = ("127.0.0.1", 9000)

# ---------------- CPU 密集 ----------------
def cpu_task():
    x = 0
    while True:
        for i in range(10000):
            x += i * i

# ---------------- IO 密集 ----------------
def io_task():
    with open(FILE_PATH, "a") as f:
        while True:
            f.write("hello perf\n")
            f.flush()
            os.fsync(f.fileno())
            time.sleep(0.001)

# ---------------- 网络服务端 ----------------
def network_server():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(SERVER_ADDR)
    s.listen(5)
    while True:
        conn, _ = s.accept()
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)

# ---------------- 网络客户端 ----------------
def network_client():
    time.sleep(1)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(SERVER_ADDR)
    while True:
        s.sendall(b"x" * 1024)
        s.recv(1024)

if __name__ == "__main__":
    threading.Thread(target=cpu_task, daemon=True).start()
    threading.Thread(target=io_task, daemon=True).start()
    threading.Thread(target=network_server, daemon=True).start()
    threading.Thread(target=network_client, daemon=True).start()

    while True:
        time.sleep(10)

运行:

bash 复制代码
python3 perf_demo.py

第一步:top ------ 确认"值得 perf"

bash 复制代码
top

你会看到:

  • python3 占用明显 CPU
  • load 有波动

👉 这一步不是分析,只是确认:机器真的在忙


第二步:perf stat ------ 定性判断瓶颈

bash 复制代码
perf stat -p $(pgrep -f perf_demo.py) sleep 10

重点看这几个:

指标 怎么理解
cycles CPU 工作量
instructions 干了多少事
IPC 每周期干几条指令
cache-misses 是否 cache 问题

运维级判断法则:

  • IPC < 1 → IO / syscall / 阻塞多
  • cycles 高但 instructions 不高 → 内核调用多

👉 这是"方向判断",不是结论


第三步:perf top ------ 实时热点(最直观)

bash 复制代码
perf top -p $(pgrep -f perf_demo.py)

你大概率会看到:

复制代码
python3        cpu_task
libc.so        write
vmlinux        tcp_sendmsg
vmlinux        recvfrom

👉 到这一步你已经知道:

  • CPU 在算什么
  • IO 在干嘛
  • 网络是不是在内核里忙

第四步:perf record + report ------ 固化证据

bash 复制代码
perf record -F 99 -p $(pgrep -f perf_demo.py) -g -- sleep 30
perf report

看 report 的核心原则:

  • Children,不是 Self
  • 排名前几的,就是问题来源

你会看到三类热点:

现象 含义
cpu_task CPU 计算热点
write / fsync IO 阻塞
tcp_sendmsg 网络 syscall

第五步:火焰图(强烈推荐)

bash 复制代码
perf script > out.perf
stackcollapse-perf.pl out.perf > out.folded
flamegraph.pl out.folded > flame.svg

怎么看火焰图(记住 3 条):

  1. 横向越宽,时间越多
  2. 最上面是"真正在跑的代码"
  3. 内核在下面,应用在上面

你会清楚看到三座"山":

  • CPU 计算山
  • IO 写文件山
  • 网络 syscall 山

实验一你应该学会什么?

✅ 不再凭感觉判断瓶颈

✅ 知道 CPU / IO / 网络在 perf 里的"长相"

✅ 能给出一句像样的结论:

perf 显示主要时间消耗在 cpu_task 计算及 write/fsync 系统调用,属于 CPU + IO 混合型负载。


实验二:锁竞争 + 网络慢的进阶分析

这个实验解决 线上最难判断的两类问题

CPU 不高,但系统很慢


实验二·场景一:制造锁竞争

在原程序中加入锁竞争

python 复制代码
lock = threading.Lock()

def lock_task():
    while True:
        with lock:
            time.sleep(0.002)

for _ in range(4):
    threading.Thread(target=lock_task, daemon=True).start()

perf 里怎么看锁?

bash 复制代码
perf top -p $(pgrep -f perf_demo.py)

你会看到:

复制代码
__pthread_mutex_lock
futex_wait
schedule

再采样:

bash 复制代码
perf record -F 99 -p $(pgrep -f perf_demo.py) -g -- sleep 20
perf report

看到 futex = 锁竞争(90% 没跑)


火焰图里的锁竞争特征

复制代码
futex_wait
 └── __pthread_mutex_lock
     └── python threading

👉 山不高,但很宽

👉 时间花在"等锁"


实验二·场景二:tcpdump + perf 分析网络慢

先用 tcpdump 看"包有没有问题"

bash 复制代码
tcpdump -i lo -nn -tttt tcp port 9000

关注:

  • 是否重传
  • ACK 是否延迟
  • Window 是否变小

再用 perf 看"慢在谁身上"

bash 复制代码
perf record -e tcp:tcp_sendmsg,tcp:tcp_recvmsg,net:netif_receive_skb \
-a -g -- sleep 20
perf report

判断逻辑:

现象 结论
tcp_recvmsg 高 应用处理慢
netif_receive_skb 高 网络/软中断
futex + schedule 应用被锁住

运维工程师级联合结论示例

tcpdump 未发现明显重传,RTT 稳定;

perf 显示大量时间消耗在 tcp_recvmsg 与 futex 等待,说明网络本身正常,瓶颈在应用处理和锁竞争。


最重要的一段(请记住)

perf 不给答案,它给证据链

标准分析路径只有一条:

复制代码
top
 ↓
perf stat(定性)
 ↓
perf top(热点)
 ↓
perf record(证据)
 ↓
火焰图(全局)
 ↓
结合代码下结论

给小白运维的一句话总结

tcpdump 证明"包怎么走"

perf 证明"时间花在哪"

火焰图让争论闭嘴


相关推荐
SmartRadio2 小时前
滚球老鼠标DIY改造成游戏光枪完整方案2
网络·游戏·计算机外设
东皇太星2 小时前
linux 内存管理详解
linux·运维·服务器
JY.yuyu2 小时前
Linux计划任务进程
linux·运维·服务器
single-life2 小时前
Linux 下 部署es+nebula(附带内网部署方式)
linux·运维·elasticsearch·nebula
last demo2 小时前
docker镜像
运维·docker·容器
ICT董老师2 小时前
kubernetes中operator与helm有什么区别?部署mysql集群是选择operator部署还是helm chart部署?
linux·运维·mysql·云原生·容器·kubernetes
catchadmin2 小时前
PHP 8.5 升级生存指南:避免凌晨两点回滚的检查清单
开发语言·php
缘来是黎2 小时前
运维面试场景题——故障排查与解决
运维·面试·职场和发展
乾元2 小时前
构建你的个人「网络 AI 实验室」——硬件、模拟器与数据集清单
运维·网络·人工智能·网络协议·架构