跳转到内容
启涵的小破站
返回

Linux 进程管理实用指南

进程管理这事儿,刚接触 Linux 的时候觉得无非就是 ps aux 看一眼、kill -9 干掉完事。干得久了才发现,真正线上出问题的时候,靠那几个基础命令根本不够用。

这篇不打算从头教你怎么用 Linux,而是把日常排查中真正管用的进程管理命令和技巧串一遍——有些你可能知道但没深挖,有些可能是实战里踩坑换来的经验。


ps:不止是 aux

ps aux 是大部分人最熟悉的,但工作中最常用的是这个组合:

ps auxf

加个 f 参数,输出变成树状结构——你能一眼看出哪个进程是哪个 fork 出来的。排查僵尸进程或者找父进程归属时特别好用。

另一个实用的:

ps -eo pid,ppid,%cpu,%mem,rss,vsz,comm,user,start,time --sort=-%cpu | head -20

自定义输出字段,按 CPU 倒序。可以做成 alias:

alias pstop='ps -eo pid,ppid,%cpu,%mem,rss,vsz,comm,user,start,time --sort=-%cpu | head -20'

线上查哪个进程在吃资源时,比 top 快,输出也更干净。

top/htop

top 默认按 CPU 排序,输入 M 按内存排序,输入 k 可以直接杀进程。

环境允许的话装个 htop 也不亏:

apt install htop

htop 的优势:鼠标可操作、彩色显示、可以选中多个进程同时操作、F5 切换树形视图。适合在开发机和测试机上用。

不过生产环境不一定有 htop,top 还是得会。

pgrep/pkill:按名字找进程

大部分人杀进程还是 ps aux | grep xxx 拿到 PID 再 kill。其实一行搞定:

pgrep -f "nginx"
pkill -f "nginx"

-f 表示匹配完整命令行。不加 -f 只匹配进程名。

更狠一点的用法:

pkill -9 -f "java -jar myapp.jar"

匹配到就杀,不需要手动记 PID。写脚本的时候非常干净。

kill 的信号表,不要只认识 9

信号数字用途
SIGTERM15优雅终止,进程可以清理资源后退出
SIGKILL9强制杀死,进程无法捕获
SIGINT2中断,等价于 Ctrl+C
SIGQUIT3退出并生成 core dump
SIGHUP1挂起,常用于让 daemon 重载配置
SIGSTOP19暂停进程(无法捕获)
SIGCONT18恢复暂停的进程

实战中最常见的情景:

写服务管理脚本的话,优雅停机可以这样来:

kill -15 $PID
sleep 5
if kill -0 $PID 2>/dev/null; then
    kill -9 $PID
fi

先给进程 5 秒做清理,没退出再强制杀。

nohup 和 disown

nohup long-running-command &

nohup 让进程忽略 SIGHUP,关掉终端后继续跑。输出重定向到 nohup.out

更好的做法是显式指定输出文件:

nohup ./script.sh > output.log 2>&1 &

如果已经跑了某个进程,忘了加 nohup,可以用 disown:

# Ctrl+Z 暂停进程
bg            # 放到后台继续跑
disown        # 从当前 shell 的任务表中移除

之后关终端也没事了。

/proc:活着的进程信息宝藏

每个进程在 /proc 下都有一个目录。实战中这些文件很常用:

cat /proc/$PID/cmdline       # 完整启动命令
cat /proc/$PID/environ       # 环境变量
cat /proc/$PID/status        # 进程状态、内存、线程数
cat /proc/$PID/limits        # 资源限制
ls /proc/$PID/fd/            # 打开的文件描述符
ls /proc/$PID/cwd/           # 进程当前工作目录

查端口占用时,sslsof 不够直观,可以直接读:

ls -l /proc/$PID/fd/ | grep socket

lsof:谁在用这个文件/端口

lsof -i :80               # 谁在监听 80 端口
lsof -p $PID              # 某个进程打开了哪些文件
lsof +D /var/log          # 某个目录下哪些文件被打开了

查端口占用最常用:

lsof -i :8080

或者用 ss 替代(更快):

ss -tlnp | grep :8080

strace:看不到的细节都在这里

进程没报错但就是不对,或者你想知道它在等什么——strace 是你的最后手段。

strace -p $PID                    # 追踪正在运行的进程
strace -p $PID -e trace=network   # 只看网络相关的系统调用
strace -p $PID -e trace=file      # 只看文件操作
strace -p $PID -T -c              # 统计各系统调用的耗时

实战一:查进程为什么卡住

strace -p $(pgrep -f "myapp")

如果输出一直停在 read(connect( 之类的调用上,说明进程在等 I/O 或网络——不是死锁,就是外部依赖慢了。

实战二:查 config 文件读哪去了

strace -e trace=open,openat,stat -f ./your-program 2>&1 | grep config

看程序启动时打开的是哪个路径的配置文件——经常能发现读错了路径这种低级但隐蔽的 bug。

僵尸进程处理

僵尸进程(Z)是子进程已死但父进程没调用 wait() 回收。表现为 ps aux 里状态是 Z,而且杀不掉。

排查:

ps -eo pid,ppid,stat,comm | grep Z

找到僵尸进程的 PID 和 PPID(父进程),然后:

  1. 尝试给父进程发 SIGCHLD:kill -17 $PPID
  2. 不行就直接杀父进程:kill -9 $PPID
  3. 如果父进程是 init(PID=1),只能重启

预防:写程序时记得处理 SIGCHLD,或者在子进程结束后调用 waitpid()

总结

进程管理说穿了就这些东西,不比别的命令更高深。线上出问题的时候知道看什么、用什么工具,比临时搜命令靠谱得多。

我自己最常用的排查路子:

  1. ps auxfpstop 看一眼整体情况
  2. CPU/内存异常 → tophtop 定位
  3. 进程卡住 → strace -p 看系统调用
  4. 端口问题 → lsof -i :portss -tlnp
  5. 杀掉异常进程 → 先 SIGTERM,不行再 SIGKILL

上面提到的命令和技巧,平时可以自己在测试环境多试试。用熟了,排查问题的时候就是本能反应了。


分享本文:

上一篇
systemd journal 使用指北
下一篇
用Astro搭建个人博客:从零到上线全记录

加载评论区中...