Linux 进程管理与性能排查:10 个核心工具实战

说明: 本文为配置思路与示例整理,不代表作者已在自己的服务器上逐项验证全部命令。执行涉及公网暴露、账户权限、数据删除或服务重启的操作前,请先备份,并结合官方文档与实际环境核验。

服务器 CPU 飙到 100%、内存被吃光、磁盘 IO 暴涨——这些问题每天都在发生。掌握进程管理与性能排查工具,是每个运维和开发者的必备技能。本文用真实场景教你 10 个核心工具,从发现问题到定位根因按步骤。

为什么需要掌握进程管理?

想象一下这个场景:凌晨 3 点,你的手机收到告警——服务器响应超时。登上服务器一看:

TEXT
$ uptime
 03:00:01 up 45 days, 3:22, 1 user, load average: 28.47, 22.13, 15.86

Load average 超过核心数好几倍,系统几乎瘫痪。这时候你该怎么办?逐个尝试 kill 吗?太慢了。你需要一套系统化的排查流程。

本文将覆盖 10 个核心工具,帮你快速定位问题:

工具 用途 适用场景
top / htop 实时监控进程资源 快速查看谁在吃资源
ps 查看进程详情 精确筛选特定进程
pidstat 进程级性能统计 持续监控单个进程
lsof 打开文件列表 排查文件句柄泄漏
strace 系统调用追踪 排查程序卡死原因
vmstat 系统整体性能 判断瓶颈类型
iostat 磁盘 IO 统计 排查磁盘性能问题
nice / renice 调整进程优先级 降低关键进程 CPU 优先级
nohup / screen / tmux 后台运行进程 防止进程随终端断开退出
systemd 服务生命周期管理 进程守护与自启动

场景一:CPU 100% 怎么排查?

第一步:用 top 快速定位

BASH
top

按下 P(大写)按 CPU 使用率排序,你会看到类似这样的输出:

TEXT
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 3847 root      20   0  892340 312068  18232 R 100.0   3.1  45:23.01 java
 1023 www       20   0  456120 128904  12056 S  12.3   1.3   8:45:33 nginx

java 进程吃满了 CPU。记下它的 PID:3847。

第二步:用 ps 查看完整命令行

BASH
ps -fp 3847 -o pid,ppid,user,%cpu,%mem,start,time,cmd
TEXT
  PID  PPID USER     %CPU %MEM  STARTED     TIME CMD
 3847  1001 root    100.0  3.1 Apr28   45:23:01 java -Xmx2g -jar app.jar

第三步:用 strace 看它在干什么

BASH
# 追踪 10 秒,看系统调用统计
strace -c -p 3847 -e trace=all &
sleep 10
kill %1

输出类似:

TEXT
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 45.23    2.345678         123     19070           futex
 23.11    1.201234          89     13497           epoll_wait
 12.45    0.647890          45     14397           read
  8.67    0.451234          67      6735           write
  5.34    0.277890          34      8173           mmap
  3.12    0.162345          28      5798           munmap
  2.08    0.108234          21      5154           close
------ ----------- ----------- --------- --------- ----------------
100.00    5.194545                 72824 total

关键发现:大量 futex 调用 = 程序在疯狂抢锁,很可能是 死锁锁竞争

第四步:查看 Java 线程堆栈

BASH
jstack 3847 > /tmp/thread_dump.txt
grep -A 20 "BLOCKED" /tmp/thread_dump.txt | head -60

这样就能找到具体是哪些线程在等锁。

场景二:内存被吃光了

用 free 快速判断

BASH
free -h
TEXT
              total        used        free      shared  buff/cache   available
Mem:           15Gi       14Gi        98Mi       256Mi       1.2Gi       856Mi
Swap:         4.0Gi       3.8Gi        20Mi

危险信号:可用内存只剩 856Mi,Swap 已用 3.8G。

找出内存大户

BASH
# 按 RSS(实际物理内存)排序,取前 10
ps aux --sort=-%mem | head -11
TEXT
USER       PID %CPU %MEM    RSS   VSZ TTY      STAT START   TIME COMMAND
root      3847 100.0 18.2 2891200 6401000 ?    Sl   Apr28  45:23 java -Xmx2g -jar app.jar
root      5621  2.3  8.5 1345600 2890000 ?     S    03:00  0:45 node server.js
www       1023  12.3  5.1  812340  456120 ?    S    Apr28   8:45 nginx: worker

用 top 的 M 键排序

top 界面按 M(大写),按内存排序查看。

检查是否有内存泄漏

BASH
# 每 5 秒记录一次进程内存使用
while true; do
  ps -p 3847 -o pid,rss,vsz,etime --no-headers
  sleep 5
done

如果 RSS 持续增长不回落,就是内存泄漏。

场景三:磁盘 IO 飙高

用 iostat 看磁盘状态

BASH
# 每 2 秒刷新一次,共 5 次
iostat -xz 2 5

重点关注这几列:

TEXT
Device   r/s    w/s    rkB/s    wkB/s    await   svctm   %util
sda    156.00  234.00  4896.00  15680.00  45.23   3.12    98.50
sdb      0.00    0.00     0.00      0.00    0.00   0.00     0.00

关键指标

  • %util > 80%:磁盘已经接近满负荷
  • await > 10ms:IO 延迟偏高
  • r/s + w/s:每秒读写次数

找出谁在大量读写磁盘

BASH
# 安装 iotop(需要 root)
apt install iotop -y
iotop -oP
TEXT
Total DISK READ:  15.32 M/s | Total DISK WRITE:  42.89 M/s
  PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
 3847 be/4  root     15.32 M/s   42.89 M/s  0.00 %  98.50 %  java -Xmx2g -jar app.jar

用 lsof 找打开的文件

BASH
# 找进程打开的文件数
lsof -p 3847 | wc -l

# 找进程打开的特定类型文件
lsof -p 3847 | grep -E '\.(log|tmp|dat)' | head -20

# 找占用某目录的进程
lsof +D /var/log/ | head -20

场景四:进程卡死不响应

用 strace 追踪系统调用

BASH
# 追踪进程的实时系统调用
strace -p 3847 -f -e trace=network,read,write

如果输出停住不动,说明进程卡在某个系统调用上。

常见卡死原因

现象 strace 输出 原因
卡在 futex futex(0x7fff..., FUTEX_WAIT, ...) 死锁
卡在 read read(5, ... 等待网络数据/管道
卡在 epoll_wait epoll_wait(7, ...) 正常空闲(不一定卡死)
卡在 connect connect(5, {sa_family=AF_INET, ...}) 网络连接超时
卡在 nanosleep nanosleep({tv_sec=300, ...}) 正在 sleep

场景五:进程优先级管理

调低非关键进程的 CPU 优先级

BASH
# 降低进程的调度优先级(-20 最高,19 最低)
renice 15 -p 3847

# 或者用 nice 启动时指定
nice -n 15 java -jar background-task.jar &

# 极端情况:暂停/恢复进程
kill -STOP 3847   # 暂停
kill -CONT 3847   # 恢复

真实场景

假设你有一个批量数据处理脚本和一个 Web 服务跑在同一台机器上:

BASH
# 数据处理脚本降低优先级
nice -n 15 python3 batch_process.py &

# Web 服务保持默认优先级
java -jar web-app.jar &

# Web 服务紧急降级(如果数据处理脚本影响了 Web 响应)
renice 15 -p $(pgrep -f batch_process.py)

实用技巧速查

批量操作

BASH
# 杀掉所有匹配的进程
pkill -f "old-process-name"

# 杀掉某个用户的所有进程
killall -u username

# 按 CPU 使用率杀进程(>90% 的非关键进程)
ps aux | awk '$3 > 90 && $11 != "java" {print $2}' | xargs -r kill

进程树查看

BASH
# 查看进程树
pstree -p

# 查看某个进程的子进程
pstree -p 1

# 查看某个进程的父进程
ps -o ppid= -p 3847

后台进程管理

BASH
# 方法一:nohup + &(最简单)
nohup java -jar app.jar > /var/log/app.log 2>&1 &

# 方法二:screen(可重新连接)
screen -S myapp
java -jar app.jar
# Ctrl+A, D 分离
# screen -r myapp 重新连接

# 方法三:tmux(screen 的现代替代)
tmux new -s myapp
java -jar app.jar
# Ctrl+B, D 分离
# tmux attach -t myapp 重新连接

# 方法四:systemd(生产环境推荐)
# 见下文

systemd 服务管理(生产环境首选)

创建一个 systemd 服务文件:

BASH
sudo vim /etc/systemd/system/myapp.service
INI
[Unit]
Description=My Application
After=network.target
Wants=network-online.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/java -Xmx2g -jar /opt/myapp/app.jar
ExecStop=/bin/kill -TERM $MAINPID
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal

# 安全限制
LimitNOFILE=65535
LimitNPROC=4096

[Install]
WantedBy=multi-user.target
BASH
# 启用并启动服务
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp

# 查看状态和日志
sudo systemctl status myapp
sudo journalctl -u myapp -f

一键排查脚本

最后分享一个我常用的快速排查脚本:

BASH
#!/bin/bash
# quick-diag.sh - 服务器快速诊断

echo "========== 系统负载 =========="
uptime

echo -e "\n========== 内存使用 =========="
free -h

echo -e "\n========== 磁盘使用 =========="
df -h | grep -E '^/dev'

echo -e "\n========== CPU TOP 5 =========="
ps aux --sort=-%cpu | head -6

echo -e "\n========== 内存 TOP 5 =========="
ps aux --sort=-%mem | head -6

echo -e "\n========== IO 最高的磁盘 =========="
iostat -x 1 1 | grep -E '^(sd|vd|nvme)'

echo -e "\n========== 最近的 OOM 记录 =========="
dmesg | grep -i "out of memory" | tail -5

echo -e "\n========== 文件描述符使用 =========="
echo "系统级: $(cat /proc/sys/fs/file-nr)"
for pid in $(ps -eo pid --no-headers | head -10); do
  count=$(ls /proc/$pid/fd 2>/dev/null | wc -l)
  name=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ' | head -c 50)
  [ "$count" -gt 100 ] && echo "PID $pid ($count fd): $name"
done
BASH
chmod +x quick-diag.sh
./quick-diag.sh

总结

场景 推荐工具链 关键命令
CPU 100% toppsstracejstack top -Hp PID
内存泄漏 freeps aux --sort=-%mem watch -n 5 'ps -p PID -o rss'
磁盘 IO 高 iostatiotoplsof iostat -xz 2
进程卡死 strace -p PID 看卡在哪个系统调用
优先级调整 nicerenice renice 15 -p PID
后台运行 nohup / tmux / systemd 生产用 systemd
快速诊断 一键脚本 uptime + free + ps

掌握这些工具后,遇到服务器问题不再是"重启试试",而是5 分钟内精准定位根因

💡 建议收藏本文,遇到问题时对照排查。如果有更好的排查技巧,欢迎留言分享!

评论