「零壹运维 · 零到壹,永不宕」
★进程挂了没人知道,服务宕机才发现——这是没做好进程管理的代价。本文从 ps/kill 这两个最基础的工具出发,到 systemctl 服务化管理,再到 Supervisor 守护非系统进程,覆盖 Linux 进程管理的完整链路。每个工具都配实战场景,帮你在排查问题时第一时间找到正确命令。
一、进程基础概念速览
1.1 进程 vs 线程 vs 守护进程
| |
|---|
| 进程(Process) | |
| 线程(Thread) | |
| 守护进程(Daemon) | 后台运行、无终端的服务进程,通常以 d 结尾(sshd/nginx) |
| 僵尸进程(Zombie) | 已退出但父进程未回收的进程,占用 PID 但不占 CPU/内存 |
| 孤儿进程(Orphan) | 父进程先退出,由 init/systemd(PID 1)接管 |
1.2 进程状态一览
R Running 正在运行或等待运行S Sleeping 可中断睡眠(等待事件,如网络 IO)D Disk Sleep 不可中断睡眠(等待磁盘 IO,无法被 kill)T Stopped 已暂停(被 SIGSTOP 信号暂停)Z Zombie 僵尸进程I Idle 内核线程空闲态
二、ps:进程快照查看
2.1 核心参数
ps aux # 显示所有进程(BSD 风格,最常用)ps -ef # 显示所有进程(System V 风格)ps -eLf # 显示所有线程# 参数含义(aux)# a 显示所有用户的进程# u 以用户友好格式显示(含 CPU/MEM 列)# x 显示无终端的进程(含守护进程)
2.2 输出字段解读
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDroot 1 0.0 0.1 171768 13456 ? Ss Mar28 0:05 /sbin/initwww-data 1234 2.3 0.8 856344 68912 ? S 14:00 1:20 nginx: worker
| |
|---|
PID | |
%CPU | |
%MEM | |
VSZ | |
RSS | |
STAT | |
TIME | |
2.3 高频实战命令
# 按 CPU 使用率排序(找 CPU 杀手)ps aux --sort=-%cpu | head -10# 按内存使用率排序(找内存大户)ps aux --sort=-%mem | head -10# 查找特定进程ps aux | grep nginxpgrep -a nginx # 更简洁,直接返回 PID 和命令# 查看进程树(显示父子关系)pstree -ppstree -p <pid># 查看某进程的所有线程ps -Lf <pid>ps -T -p <pid> # top 风格# 查看僵尸进程ps aux | awk '$8 == "Z"'# 查看进程启动时间ps -p <pid> -o pid,lstart,etime,cmd
2.4 /proc 目录:进程信息宝库
# 进程详细信息cat /proc/<pid>/status # 状态、内存、线程数cat /proc/<pid>/cmdline # 完整启动命令(\0 分隔)cat /proc/<pid>/environ # 环境变量ls -la /proc/<pid>/fd # 打开的文件描述符cat /proc/<pid>/net/tcp # 网络连接# 实用示例:查看进程完整命令tr '\0'' ' < /proc/<pid>/cmdline && echo# 查看进程工作目录ls -la /proc/<pid>/cwd
三、kill:信号发送与进程控制
3.1 信号速查表
kill -l # 列出所有信号
| | | |
|---|
SIGHUP | | | |
SIGINT | | | |
SIGQUIT | | | |
SIGKILL | | 强制杀死,不可拦截 | |
SIGTERM | | | |
SIGSTOP | | | |
SIGCONT | | | |
SIGUSR1 | | | |
3.2 正确的 kill 姿势
# 优先使用 SIGTERM(给进程机会优雅退出)kill <pid> # 默认发送 SIGTERM (15)kill -15 <pid> # 同上,显式指定# 优雅退出等待几秒后,再用 SIGKILLkill -9 <pid> # 强制杀死(最后手段,数据可能丢失)# ❌ 不要上来就 kill -9,会导致:# - 文件未正确关闭(数据损坏)# - 锁文件未清理(导致下次无法启动)# - 子进程变孤儿
3.3 批量操作
# 按进程名杀死(pkill 更安全,不影响其他用户)pkill nginx # 发送 SIGTERMpkill -9 nginx # 强制杀死pkill -u www-data nginx # 只杀 www-data 用户的 nginx# killall 按名称杀死所有匹配进程killall nginxkillall -9 java# 向进程组发送信号kill -TERM -<pgid> # 负数 PID 表示进程组# 发送 HUP 让 Nginx 重新加载配置(不重启)kill -HUP $(cat /var/run/nginx.pid)nginx -s reload # 等效命令
3.4 处理 D 状态进程(不可中断睡眠)
# D 状态进程无法被 kill -9 杀死!ps aux | awk '$8 ~ /D/ {print $0}'# 原因:进程在等待内核 IO 完成,kill 信号排队等待# 解决方案:# 1. 等待 IO 完成(最安全)# 2. 排查导致 IO 挂起的原因(NFS 卡住、磁盘故障)# 3. 重启系统(最后手段)
四、systemctl:服务化管理
4.1 核心命令速查
# 服务控制systemctl start <service> # 启动systemctl stop <service> # 停止systemctl restart <service> # 重启systemctl reload <service> # 重新加载配置(不中断服务)systemctl status <service> # 查看状态# 开机自启systemctl enable <service> # 设为开机启动systemctl disable <service> # 取消开机启动systemctl is-enabled <service> # 查看是否开机启动# 服务列表systemctl list-units --type=service # 所有服务systemctl list-units --type=service --failed # 失败的服务systemctl list-unit-files --type=service # 所有服务文件
4.2 编写 systemd Unit 文件
将应用注册为系统服务:
# /etc/systemd/system/myapp.service[Unit]Description=My Application ServiceAfter=network.target # 网络启动后再启动本服务Wants=network-online.target[Service]Type=simple # simple/forking/oneshot/notifyUser=appuser # 运行用户(不要用 root)Group=appuserWorkingDirectory=/opt/myapp # 工作目录ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yamlExecReload=/bin/kill -HUP $MAINPID # reload 时发送 HUP 信号ExecStop=/bin/kill -TERM $MAINPID# 重启策略Restart=on-failure # 失败时自动重启(always/on-failure/no)RestartSec=5 # 重启前等待 5 秒StartLimitBurst=3 # 10 分钟内最多重启 3 次StartLimitIntervalSec=600# 资源限制LimitNOFILE=65535 # 最大文件描述符MemoryLimit=512M # 内存上限(systemd v231+)# 环境变量Environment=NODE_ENV=productionEnvironmentFile=/etc/myapp/env # 从文件读取环境变量# 标准输出重定向StandardOutput=journal # 输出到 systemd journalStandardError=journal[Install]WantedBy=multi-user.target # 多用户模式下启用
# 注册并启动服务systemctl daemon-reload # 重新加载 unit 文件systemctl enable --now myapp # 设置开机自启并立即启动# 验证systemctl status myapp
4.3 Service Type 详解
| | |
|---|
simple | | |
forking | | |
oneshot | | |
notify | | |
4.4 查看服务日志
# 查看服务最新日志journalctl -u myapp -n 50# 实时跟踪日志journalctl -u myapp -f# 查看今天的日志journalctl -u myapp --since today# 查看指定时间范围journalctl -u myapp --since "2026-03-30 10:00" --until "2026-03-30 15:00"# 查看上次启动的日志journalctl -u myapp -b -1# 只看错误和警告journalctl -u myapp -p err
4.5 常见问题排查
# 服务启动失败systemctl status myapp # 先看状态journalctl -u myapp -n 30 --no-pager # 看完整日志# 常见原因:# - ExecStart 路径错误# - 权限不足(User 设置不对)# - 端口被占用# - 依赖服务未启动# 服务频繁重启被限制(start limit hit)# 现象:systemctl status 显示 "start request repeated too quickly"systemctl reset-failed myapp # 重置失败计数systemctl start myapp# 根本解决:排查崩溃原因,调大 StartLimitBurst
五、Supervisor:守护非系统进程
5.1 为什么需要 Supervisor
systemd 适合管理系统级服务,但有些场景更适合 Supervisor:
- 需要管理多个子进程(Python/Node 多进程应用)
5.2 安装与启动
# Ubuntu/Debianapt install supervisor -y# CentOS/RHELyum install supervisor -y# 或 pip 安装pip install supervisor# 启动systemctl start supervisorsystemctl enable supervisor# 查看版本supervisord --versionsupervisorctl --version
5.3 配置文件结构
# /etc/supervisor/supervisord.conf(主配置)[unix_http_server]file=/var/run/supervisor.sock[supervisord]logfile=/var/log/supervisor/supervisord.logpidfile=/var/run/supervisord.pid[rpcinterface:supervisor]supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface[supervisorctl]serverurl=unix:///var/run/supervisor.sock[include]files = /etc/supervisor/conf.d/*.conf # 子配置目录
5.4 程序配置示例
# /etc/supervisor/conf.d/myapp.conf[program:myapp]command=/opt/myapp/bin/myapp --port=8080 ; 启动命令directory=/opt/myapp ; 工作目录user=appuser ; 运行用户autostart=true ; supervisor 启动时自动启动autorestart=true ; 异常退出自动重启startsecs=5 ; 启动后稳定 5 秒才算成功startretries=3 ; 最多重试 3 次; 日志配置stdout_logfile=/var/log/myapp/stdout.logstdout_logfile_maxbytes=50MB ; 单文件最大 50MBstdout_logfile_backups=5 ; 保留 5 个历史文件stderr_logfile=/var/log/myapp/stderr.logstderr_logfile_maxbytes=50MB; 环境变量environment=NODE_ENV="production",PORT="8080"; 进程优先级(数字越小越先启动)priority=100
5.5 多进程配置(numprocs)
# 启动 4 个 worker 进程[program:worker]command=/opt/myapp/worker.py --id=%(process_num)02dnumprocs=4 ; 启动 4 个实例process_name=%(program_name)s_%(process_num)02d ; worker_00, worker_01...autostart=trueautorestart=truestdout_logfile=/var/log/worker/%(program_name)s_%(process_num)02d.log
5.6 supervisorctl 常用命令
# 查看所有进程状态supervisorctl status# 控制单个程序supervisorctl start myappsupervisorctl stop myappsupervisorctl restart myapp# 重新加载配置(不重启已运行程序)supervisorctl reread # 重新读取配置文件supervisorctl update # 应用变更(新增/修改的程序)# 全部操作supervisorctl start allsupervisorctl stop allsupervisorctl restart all# 查看实时日志supervisorctl tail -f myapp stdoutsupervisorctl tail -f myapp stderr
六、进程管理完整 SOP
6.1 服务异常排查流程
服务无响应 │ ├─ systemctl status <svc> / supervisorctl status │ ├─ Active: failed → journalctl 看错误日志 │ ├─ Active: inactive → 未启动,手动 start │ └─ Active: active 但无响应 → 进程假死 │ ├─ ps aux | grep <name> │ ├─ 不存在 → 进程崩溃,查日志找原因 │ ├─ Z 状态 → 僵尸进程,查父进程 │ └─ D 状态 → IO 挂起,查磁盘/NFS │ ├─ 进程存在但无响应 │ ├─ strace -p <pid> 查系统调用卡在哪 │ ├─ lsof -p <pid> 查文件/网络描述符 │ └─ kill -9 后重启服务 │ └─ 端口监听问题 ss -tlnp | grep <port> lsof -i :<port>
6.2 优雅重启脚本模板
#!/bin/bashSERVICE="myapp"PID_FILE="/var/run/myapp.pid"MAX_WAIT=30# 发送 SIGTERM,等待优雅退出if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE")echo"Stopping $SERVICE (PID: $PID)..."kill -TERM "$PID"# 等待进程退出for i in $(seq 1 $MAX_WAIT); doif ! kill -0 "$PID" 2>/dev/null; thenecho"$SERVICE stopped gracefully"breakfi sleep 1if [ $i -eq $MAX_WAIT ]; thenecho"Force killing $SERVICE..."kill -9 "$PID"fidonefi# 重新启动echo"Starting $SERVICE..."systemctl start "$SERVICE"systemctl status "$SERVICE"
七、命令速查卡
# psps aux --sort=-%cpu | head -10 # CPU Top10ps aux --sort=-%mem | head -10 # 内存 Top10ps aux | awk '$8 == "Z"'# 找僵尸进程pstree -p <pid> # 进程树# kill / pkillkill -15 <pid> # 优雅退出kill -9 <pid> # 强制杀死pkill -u <user> <name> # 杀指定用户的进程kill -HUP <pid> # 重载配置# systemctlsystemctl status/start/stop/restart/reload <svc>systemctl enable/disable <svc>journalctl -u <svc> -f # 实时日志journalctl -u <svc> -p err -n 50 # 最近 50 条错误# supervisorctlsupervisorctl status # 全部状态supervisorctl start/stop/restart <prog>supervisorctl reread && supervisorctl update # 重载配置supervisorctl tail -f <prog> stdout # 实时日志
八、最佳实践
优先 SIGTERM,最后才用 SIGKILL:给程序机会做清理工作(关数据库连接、刷缓冲区),SIGKILL 是最后手段。
服务化所有后台程序:不要用 nohup & 跑服务,用 systemd 或 Supervisor 托管,保证崩溃自动重启、开机自启、日志集中管理。
systemd 服务不要用 root 运行:在 Unit 文件里指定 User= 和 Group=,最小权限原则。
设置资源限制:在 Unit 文件加 LimitNOFILE、MemoryLimit,防止单个服务耗尽系统资源。
配置重启策略和次数限制:Restart=on-failure + StartLimitBurst=3 组合,既保证自动恢复,又防止崩溃循环打爆系统。
Supervisor 日志配置 maxbytes:不设置大小限制的日志文件会撑满磁盘,一定要配 stdout_logfile_maxbytes 和 backups。
定期检查僵尸进程:ps aux | awk '$8=="Z"',僵尸多说明父进程有 bug,没有正确调用 wait()。
九、总结
| | |
|---|
ps | | |
kill/pkill | | |
systemctl | | |
supervisorctl | | |
进程管理的核心是:让服务跑起来、跑稳了、崩了能自愈、出了问题能快速定位。四个工具各司其职,组合使用覆盖从排查到管控的全链路。
★一个健壮的生产系统,不是不会挂,而是挂了没人察觉——因为它已经自己重启好了。