运维工程师的 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 证明"时间花在哪"

火焰图让争论闭嘴


相关推荐
REDcker10 小时前
RTSP 直播技术详解
linux·服务器·网络·音视频·实时音视频·直播·rtsp
代码游侠11 小时前
学习笔记——Linux内核与嵌入式开发1
linux·运维·前端·arm开发·单片机·嵌入式硬件·学习
阿猿收手吧!11 小时前
【C++】异常处理:catch块执行后程序如何继续
服务器·网络·c++
腾讯蓝鲸智云11 小时前
【运维自动化-节点管理】节点管理跟配置平台的联动关系
运维·服务器·经验分享·自动化·sass·paas
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.11 小时前
Nginx构建PC站点:root与alias详解
运维·chrome·nginx
FLGB11 小时前
Docker网段和服务器内部网段172.17 网段冲突导致网络不通
服务器·网络·docker
星夜落月12 小时前
Web-Check部署全攻略:打造个人网站监控与分析中心
运维·前端·网络
每次学一点12 小时前
【ZeroTier自研之路】planet的组成
服务器·网络·数据库
Turboex邮件分享12 小时前
邮件投递全流程故障排查手册
运维·人工智能
hwj运维之路12 小时前
超详细ubuntu22.04部署k8s1.28高可用(二)【结合ingress实现业务高可用】
运维·云原生·容器·kubernetes