进程行为异常时,光看日志不够:程序卡住不知道在等什么、文件句柄泄露不知道为什么、CPU 飙高不知道耗在哪。
这篇讲三个底层调试工具:strace(看系统调用)、lsof(看文件句柄)、perf(看 CPU 热点)。不需要改代码,就能把黑盒程序看穿。
需要云服务器可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上,主流游戏快速部署。
一、什么时候用哪个
| 症状 | 工具 | 看什么 |
|---|---|---|
| 程序卡住不动 | strace -p PID | 卡在哪个系统调用 |
| 句柄数暴涨、报 too many open files | lsof -p PID | 开着哪些文件/连接 |
| CPU 100% 但不知道哪段代码 | perf top | 热点函数 |
| 程序启动就退出,不知道原因 | strace 程序名 | 退出前最后一个调用 |
| 端口被占用找不到进程 | lsof -i :端口 | 谁占的 |
| 内存只涨不降 | pmap + lsof | 内存映射和泄漏的 fd |
二、strace:跟踪系统调用
strace 记录程序和内核之间的每一次交互:打开什么文件、连什么网络、读什么、卡在哪。
1. 程序卡住了:看它在等什么
# 找到进程 PID
pgrep -f PalServer
# attach 上去看实时系统调用
strace -p PID
# 输出示例:
# futex(0x7f..., FUTEX_WAIT, ...) = -1 EAGAIN ← 在等锁
# read(5, ← 卡在读文件/网络,等数据
# nanosleep({tv_sec=1}, {tv_sec=0}) = 0 ← 在睡觉,正常空闲判读:
| 卡住的调用 | 含义 |
|---|---|
futex | 等锁(线程死锁/锁竞争) |
read/recvfrom 无返回 | 等数据(网络慢/对端不回) |
nanosleep | 正常休眠 |
wait4 | 等子进程 |
flock/fcntl | 等文件锁 |
2. 程序启动失败:看它读了什么
strace -f -e trace=file ./program 2>&1 | tail -30
# 能看到它尝试打开哪些配置文件,哪个不存在(ENOENT)导致失败游戏服启动失败的经典场景:缺 32 位库、找不到配置文件、权限不足(EACCES),strace 一眼定位。
3. 网络连接问题
# 只看网络相关调用
strace -p PID -e trace=network
# 看 DNS 解析
strace -e trace=connect,sendto,recvfrom ./program | head4. 统计模式(开销小)
# 不跟每条调用,只统计
strace -c -p PID
# 跑 10 秒 Ctrl+C,看哪类调用最多、哪类最慢常用参数
| 参数 | 作用 |
|---|---|
-p PID | attach 到运行中的进程 |
-f | 跟踪子进程/线程 |
-e trace=file | 只看文件相关调用 |
-e trace=network | 只看网络相关 |
-c | 统计摘要 |
-s 200 | 字符串显示长度(默认 32 太短) |
-t/-tt | 加时间戳 |
-o file | 输出到文件 |
注意:strace 用 ptrace,会拖慢目标进程(2–20 倍)。生产环境短时间用,别长期挂着。
三、lsof:文件句柄侦探
Linux 一切皆文件——打开的文件、网络连接、管道,都是 fd(文件描述符)。
1. 句柄泄漏排查
# 进程开了多少 fd
ls /proc/PID/fd | wc -l
# 系统上限
cat /proc/sys/fs/file-max
ulimit -n
# 具体开了什么(按类型归类)
lsof -p PID | awk '{print $5}' | sort | uniq -c | sort -rn
# 看具体的连接(找泄漏的连接)
lsof -p PID -i -a -P -n | head -30报 too many open files 时:要么调大 ulimit -n,要么程序在泄漏 fd——lsof 看增长的是哪类(通常是 socket),找程序 bug。
2. 端口占用
# 谁占了 8080
lsof -i :8080
# 所有监听端口和进程
lsof -i -P -n | grep LISTEN
# 比 ss 好在能直接看到进程名和用户3. 谁在动我的文件
# 谁打开了 /var/log/syslog
lsof /var/log/syslog
# 谁打开了删除但空间没释放的文件(经典坑!)
lsof +L1
# 输出里 SIZE/OFF 大 + NLINK 0 的,就是删了还被占着的4. 卸载磁盘报 busy
# 谁在用 /data
lsof /data
# 杀进程或 fuser -k /data5. 看进程的工作目录和加载的库
lsof -p PID | grep -E "cwd|txt|mem"
# cwd = 当前目录(找程序在哪运行)
# txt = 可执行文件(确认是不是被替换过的)四、perf:CPU 热点分析
perf 是 Linux 的性能剖析神器,直接看 CPU 周期花在哪些函数上。
1. 实时热点
# 安装
apt install -y linux-tools-common linux-tools-$(uname -r)
# 实时看(类似 top,但按函数排)
perf top
# 只看某个进程
perf top -p PID
# 看内核热点
perf top -k2. 记录并分析
# 记录 30 秒
perf record -F 99 -p PID -g -- sleep 30
# 生成报告
perf report报告里看:
- Overhead 列:该函数占的 CPU 比例
- 调用链(-g):谁调用了它
- 自己的代码 vs 库函数 vs 内核
3. 常见场景
| perf top 看到 | 结论 |
|---|---|
libc 内存函数(malloc/memcpy) | 内存操作密集 |
内核 copy_user | 频繁用户态/内核态拷贝(大量 IO) |
spin_lock/mutex | 锁竞争严重 |
| 自己的业务函数 | 算法问题,优化代码 |
sys_ioctl 高 | 频繁系统调用,批处理能优化 |
4. 容器里的 perf
容器内 perf 权限受限。两种方案:
# 1. 容器加权限
docker run --cap-add SYS_ADMIN --security-opt seccomp=unconfined ...
# 2. 在宿主机跑 perf -p 容器内进程的宿主机 PID
# (docker top 或 ps 看到宿主 PID)五、实战案例
案例 1:游戏服半夜卡死
# 1. 进程还在但没响应
pgrep -f PalServer # PID 12345
# 2. strace 看卡在哪
strace -p 12345
# futex(0x7f8a... FUTEX_WAIT ... ← 等锁
# 3. 看线程在干什么
ls /proc/12345/task/ | wc -l # 32 个线程
for t in /proc/12345/task/*; do cat $t/stack 2>/dev/null; done | sort | uniq -c
# 多个线程卡在同一个 futex = 锁竞争/死锁
# 4. perf 看是谁持有锁
perf record -p 12345 -g -- sleep 10
perf report
# 发现某个线程 spin_lock 占了 40% CPU = 死循环抢锁
# 结论: 游戏服 bug,重启临时恢复,反馈官方或换版本案例 2:句柄耗尽
# 1. 报错
tail /var/log/myapp.log
# java.net.SocketException: Too many open files
# 2. 看 fd 数在涨
watch -n 2 'ls /proc/PID/fd | wc -l'
# 从 2000 涨到 65000(ulimit 上限)
# 3. 看是什么 fd
lsof -p PID -i -a -P -n | awk '{print $8, $9}' | sort | uniq -c | sort -rn | head
# ESTABLISHED 的连接几千个不释放 = 连接泄漏
# 4. 处置
# 短期: ulimit -n 65535 + 重启
# 长期: 修程序的连接池(关闭响应不读 body 之类)案例 3:端口莫名被占
lsof -i :8211
# COMMAND PID USER FD TYPE NODE NAME
# PalServe 12345 steam 10u IPv4 12345 UDP *:8211
# 原来是上一个没杀干净的实例,重启即可六、工具组合拳速查
# 「程序卡了」排查链
pgrep -f 进程名 # 1. 找 PID
strace -p PID # 2. 看卡在哪个调用
cat /proc/PID/status | grep Thread # 3. 线程数
perf top -p PID # 4. 看 CPU 花在哪
# 「资源异常」排查链
lsof -p PID | wc -l # fd 数
ls /proc/PID/fd | wc -l # fd 数(另一种)
cat /proc/PID/limits # 该进程的资源上限
pmap -x PID | tail -1 # 内存映射总量七、常见问题
Q:strace 一 attach 进程就挂了?
ptrace 被禁(容器默认 seccomp 拦 ptrace)或 SELinux/AppArmor 限制。容器加 --cap-add SYS_PTRACE。
Q:perf 报 perf_event_paranoid?
# 降低限制(宿主机)
echo 1 > /proc/sys/kernel/perf_event_paranoid
# 或永久: echo 'kernel.perf_event_paranoid=1' >> /etc/sysctl.d/99-perf.confQ:lsof 输出太多看不到重点?
加过滤:lsof -p PID -i(只看网络)、lsof -p PID | grep REG(只看普通文件)、lsof -p PID | grep -c REG(数普通文件)。
Q:strace/perf 开销大吗?
strace 大(每条调用都拦截,2–20 倍减速),perf 小(硬件计数器采样,< 5%)。生产环境 perf 可以常开采样,strace 只短时间用。
Q:这些工具在 Windows 上有对应吗?
strace ≈ Process Monitor,lsof ≈ handle.exe/Resource Monitor,perf ≈ WPA。Linux 上这三个是标配,建议 SSH 上去看。
小结
进程调试的核心思路:卡住看 strace(等什么)、句柄异常看 lsof(占什么)、CPU 异常看 perf(耗在哪)。三个工具都不需要改代码,attach 上去就能看到进程的内幕。配合 /proc/PID/ 下的信息,90% 的疑难杂症能定位到根因。
更多运维内容见财富云计算知识库:性能分析三板斧、日志管理、进程管理、监控体系全系列。需要云服务器可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上。
延伸阅读
- 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
- 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
- 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
- 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
- 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
- RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
- 短链接服务搭建:Shlink 自托管,链接数据自己掌握
- 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道

[...]lsof +D /var/log 2>/dev/null | head # 谁开着日志文件在写更多工具组合见Linux 性能分析三板斧和进程调试实战。三、常见卡顿场景与解法3.1 游戏服存档写入卡现象:每到自动存档时刻全服卡一下,平时也偶尔顿。原因:游戏把整个世界刷盘,机械盘或低 IOPS 云盘扛不住。解法:# 1. 确认磁盘类型(控制台看,或简单测速)[...]
2026-10-15 回复
[...]# https://z0w.cn/wiki/process-debug-perf.html排查思路汇总见服务器资源排查实战,游戏场景特化版见开服卡顿掉线排查手册。七、常见疑问Q:top 里 %CPU 超过 100% 正常吗?多核机器上,一个多线程进程可以吃掉 400%(4 核全占)。按 1 展开看单核,才知道是不是单核瓶颈。Q:load average 三个数字看哪个?1 分钟看当下,5 分[...]
2026-10-18 回复
[...]netsh advfirewall firewall add rule name="GameRange" dir=in action=allow protocol=UDP localport=26901-26903完整的 Windows 环境准备(远程桌面连接、运行库安装、SteamCMD 用法、开机自启、8 个常见坑)见:《Windows 云服务器开服通用前置教程》→ htt[...]
2026-10-18 回复