Skip to content

进程与服务 ​

排障第一步永远是 ps 和 top。这一节的重点是看懂 STAT 列和选对 kill 信号。

一、ps ​

bash
ps aux | head -10
ps -ef | grep java
ps -eo pid,ppid,user,stat,%cpu,%mem,etime,cmd --sort=-%cpu | head -10
text
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1 169312 12884 ?        Ss   Oct26   0:08 /usr/lib/systemd/systemd
app      28441 42.1 12.4 4214320 2012340 ?     Sl   20:41  42:18 java -jar /app/app.jar
root     29411  0.0  0.0  11204  1024 ?        S    Oct26   0:02 /usr/bin/docker-proxy -proto tcp
字段含义
VSZ虚拟内存大小(含未实际分配的映射),通常很大,不用管
RSS实际驻留物理内存,看这个
STAT进程状态 + 附加标志
TIME累计 CPU 时间,不是运行时长
START启动时间
etime运行了多久(ps -eo etime)

STAT 怎么看 ​

第一个字母是主状态:

状态含义说明
RRunning / Runnable在跑或等 CPU
SSleeping可中断睡眠,等事件(最常见)
DUninterruptible Sleep等 IO,kill -9 也杀不掉
TStopped被 Ctrl+Z 或 SIGSTOP 暂停
ZZombie已退出但父进程没回收
IIdle内核空闲线程

附加标志:

标志含义
s会话首进程
l多线程
+在前台进程组
<高优先级
N低优先级

D 状态和 Z 状态是两个大坑:

bash
ps -eo pid,stat,wchan:30,cmd | awk '$2 ~ /^D/'
text
  PID STAT WCHAN                          CMD
31882 D    rpc_wait_bit_killable            java -jar /app/app.jar

wchan 是它卡在哪个内核函数。D 状态通常是磁盘/NFS 卡死,kill -9 无效,只能等 IO 超时或重启机器。

bash
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /^Z/'
text
  PID  PPID STAT CMD
42111     1 Z    [java] <defunct>

僵尸进程本身不占资源,但说明父进程没调 wait()。杀父进程才能回收。

二、top ​

bash
top -b -n 1 | head -20
text
top - 21:32:11 up 1 day,  3:12,  2 users,  load average: 2.41, 1.88, 1.62
Tasks: 214 total,   1 running, 213 sleeping,   0 stopped,   0 zombie
%Cpu(s): 38.2 us,  6.1 sy,  0.0 ni, 50.4 id,  5.1 wa,  0.0 hi,  0.2 si,  0.0 st
MiB Mem :  16006.2 total,   512.4 free,  13284.1 used,   2209.7 buff/cache
MiB Swap:   4096.0 total,   1204.2 used,   2891.8 free.   1150.2 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  28441 app       20   0 4214320   1.9g   182m S  42.1  12.4  42:18.11 java
  29112 mysql     20   0 2143212   1.2g    96m S   8.2   7.4  12:41.02 mysqld

关键行:

指标含义判断
load average1/5/15 分钟平均可运行 + 不可中断任务数与核数比较,超过核数就是排队
us用户态 CPU应用在算
sy内核态 CPU系统调用多(网络/IO)
wa等 IO> 20% 说明磁盘是瓶颈
si / hi软中断 / 硬中断网络包太密或驱动问题
st被虚拟化层偷走云主机超卖
avail Mem可用内存(含可回收缓存)看这个,不是 free

load average 的解读:

text
4 核机器,load 4.0  → 刚好跑满
4 核机器,load 12.0 → 平均有 8 个任务在排队
4 核机器,load 4.0 但 CPU 空闲 60% → 有 D 状态进程卡在 IO

最后一种最容易被误判:"负载高但 CPU 不高" = IO 或锁等待。

三、找进程 ​

bash
pgrep -af java                # -a 显示完整命令行
pgrep -u app -f 'app.jar'
pidof nginx
pstree -p 31882 | head
text
28441 java -jar /app/app.jar
text
java(28441)─┬─{java}(28442)
            ├─{java}(28443)
            └─{java}(28444)

pgrep -f 匹配完整命令行,pgrep java 只匹配进程名(会被截断到 15 字符)。

四、kill 信号 ​

bash
kill -15 28441      # SIGTERM,默认,请求优雅退出
kill -9 28441       # SIGKILL,强杀,进程无法捕获
kill -HUP 28441     # SIGHUP,很多服务用它来 reload 配置
kill -0 28441       # 不发信号,只检查进程是否存在($? 为 0 表示存在)
信号编号用途
SIGHUP1重载配置 / 终端断开
SIGINT2Ctrl+C
SIGQUIT3退出并 dump(Java 的线程栈)
SIGKILL9强杀,不可捕获、不可清理
SIGTERM15默认,优雅退出
SIGUSR110应用自定义(如日志切割)
SIGSTOP / SIGCONT19/18暂停 / 继续

kill -9 是最后手段

SIGKILL 不给进程任何清理机会:临时文件不删、缓冲区不 flush、数据库连接不断开、分布式锁不释放。Java 应用被 -9 后,未提交的事务、未落盘的日志全丢。

正确顺序:SIGTERM → 等 10~30 秒 → 还活着再 SIGKILL。

bash
kill -15 28441 && sleep 15 && kill -0 28441 2>/dev/null && kill -9 28441

五、优先级 ​

bash
nice -n 10 /opt/backup.sh        # 以低优先级启动
renice -n 5 -p 28441             # 调整已运行进程
ionice -c 3 -p 28441             # IO 优先级:3 = idle,最闲时才跑

nice 范围 -20(最高)到 19(最低),普通用户只能调高(降低优先级)。

备份、日志压缩这类任务用 nice -n 19 ionice -c 3,避免和业务抢资源。

六、后台运行 ​

bash
# 临时后台
./start.sh &

# 退出终端后继续跑(nohup + 重定向)
nohup ./start.sh > /var/log/start.log 2>&1 &

# 已经在前台跑了,想放到后台:Ctrl+Z 然后
bg
disown -h %1

# 完全脱离终端
setsid ./start.sh </dev/null >/var/log/start.log 2>&1 &

& 不等于守护进程

直接 ./start.sh & 在关闭 SSH 时仍会收到 SIGHUP 被杀。nohup 才是屏蔽 SIGHUP 的正确方式,且必须重定向输出,否则退出后写终端会报错。

长期服务不要用这套,用 systemd(见 systemd 单元)。

七、/proc 里的信息 ​

bash
PID=28441

cat /proc/$PID/cmdline | tr '\0' ' '; echo
ls -l /proc/$PID/cwd
tr '\0' '\n' < /proc/$PID/environ | grep -i java_opts
ls -l /proc/$PID/fd | head
cat /proc/$PID/limits | grep -i 'open files'
text
java -jar /app/app.jar

lrwxrwxrwx 1 app app 0 Oct 27 21:30 /proc/28441/cwd -> /app

JAVA_OPTS=-Xms1g -Xmx1g -XX:+UseG1GC

lrwx------ 1 app app 64 Oct 27 21:30 0 -> /dev/null
lrwx------ 1 app app 64 Oct 27 21:30 1 -> /data/app/logs/app.log
lrwx------ 1 app app 64 Oct 27 21:30 2 -> /data/app/logs/error.log

Max open files            65535                65535                files

cmdline 用 \0 分隔参数,直接 cat 会粘在一起。fd 能看到它打开了哪些文件——排查"删了文件空间不释放"就靠这个。

八、ulimit ​

bash
ulimit -n          # 文件句柄
ulimit -u          # 最大进程数
ulimit -a
text
open files                      (-n) 1024
max user processes              (-u) 31750

1024 对服务端远远不够。永久修改:

ini
# /etc/security/limits.conf
app  soft  nofile  65535
app  hard  nofile  65535

systemd 服务要在 unit 里设(limits.conf 对 systemd 服务不生效):

ini
[Service]
LimitNOFILE=65535

九、踩过的坑 ​

现象根因处置
kill -9 杀不掉进程在 D 状态等 IO 超时,或重启机器
一堆僵尸进程父进程不回收重启父进程
负载高但 CPU 空闲D 状态进程卡 IOps -eo stat,wchan 定位
ps 里 RSS 很大但内存没降共享库/页缓存计入看 PSS(smem)更准
服务重启后进程还在用了 nohup & 留下的野进程pkill -f,改 systemd
nohup 后日志为空只重定向了 stdout 没管 stderr> log 2>&1
ulimit -n 改了不生效对 systemd 服务无效在 unit 里设 LimitNOFILE
进程名匹配不到pgrep 按名字截断到 15 字符用 pgrep -f 匹配命令行