这个命令是干啥的
kill 命令的名字有点误导人。它并不是专门用来"杀"进程的。准确地说,它是给进程发信号的命令。
Linux 的进程之间可以通过信号通信。最常见的信号 SIGTERM(15)是"请结束",而 SIGKILL(9)是"立刻消失"。就像你跟一个同事说"下班了,你走吧"(SIGTERM)和直接把他推出去锁门(SIGKILL)的区别。
我第一次用 kill -9 的时候以为那就是标准的杀进程方式。后来发现不对劲:有一次我在跑一个重要的数据导入脚本,因为太慢了就想杀掉重来。kill -9 下去,进程是没了,但数据库里的数据处于半导入状态,坏了好几条记录。
后来前辈告诉我:kill -15 先问能不能停,kill -9 是最后手段。
说实话,你回想一下自己是不是遇到问题就 kill -9 PID?我以前也是。现在我的原则变了:能友好结束就友好结束,实在不听话才动粗。
基本用法(3分钟上手)
kill -15 SIGTERM(默认信号)
# 向进程 1234 发送 SIGTERM 信号(进程自行退出)
kill 1234
# 跟 kill -15 1234 一样,因为 15 是默认信号
kill -15 1234
进程收到 SIGTERM 后,会:
1. 保存当前数据
2. 关闭打开的文件
3. 清理临时文件
4. 然后退出
大部分服务(Nginx、SSH、MySQL 等)都会响应这个信号,优雅退出。
kill -9 SIGKILL(强制终结)
# 强制杀掉进程 1234
kill -9 1234
# 也可以用 -SIGKILL
kill -SIGKILL 1234
SIGKILL 是个特例:进程不能忽略、不能捕获、不能处理这个信号。内核直接把这个进程从进程表里移除。它来不及保存任何数据,打开的文件也没机会关闭。
所以我说 kill -9 是下策。进程死了,但可能留下一堆烂摊子。
kill -1 SIGHUP(重载配置)
# 让 Nginx 重新加载配置
kill -1 $(pgrep -x nginx | head -1)
# 或者是 Nginx 主进程的 PID
kill -1 1234
# 更直接的 reload 方式(但效果一样)
systemctl reload nginx
SIGHUP 的原始含义是"终端挂断了"。后来很多守护进程把它的含义变成了"重新加载配置文件"。所以修改 Nginx 或 SSH 配置后,用 kill -1 可以热加载配置,不需要重启服务。
# 修改了 Nginx 配置后,重载
sudo kill -1 $(cat /var/run/nginx.pid)
# 注意:这里发给 Nginx master 进程,不是 worker
kill -0(存活检测)
# 检查进程 1234 是否存在,存在返回 0,不存在返回 1
kill -0 1234
# 查看返回值
echo $?
kill -0 不发送任何实际的信号,只是检查进程是否存在。这常用于脚本里的判断:
# 监控脚本里检查进程是否活着
if kill -0 $(cat /var/run/myservice.pid) 2>/dev/null; then
echo "服务运行正常"
else
echo "服务挂了,发送告警"
fi
进阶骚操作
信号编号大全
常见信号速查表(记前 5 个就够日常用了):
| 编号 | 名称 | 用途 |
|---|
| 1 | SIGHUP | 重载配置 / 终端断线 |
| 2 | SIGINT | Ctrl+C 中断 |
| 3 | SIGQUIT | Ctrl+\ 退出(含 core dump) |
| 9 | SIGKILL | 强制杀掉(不能忽略) |
| 15 | SIGTERM | 优雅结束(默认信号) |
| 18 | SIGCONT | 继续被暂停的进程 |
| 19 | SIGSTOP | 暂停进程(不能忽略) |
| 20 | SIGTSTP | Ctrl+Z 暂停进程 |
# 列出所有可用信号
kill -l
# 列出部分
kill -l TERM KILL HUP
杀不死进程的 5 个原因
有时候你 kill -9 下去,进程还在。这不是你网不行,是某些进程真的杀不死。常见的原因:
#### 1. 僵尸进程
# 检查僵尸进程
ps aux | grep ' Z '
僵尸进程已经死了,但父进程没给它"收尸"。kill 它没用。因为它已经死了。解决办法是杀掉它的父进程(PPID)。
#### 2. 磁盘 I/O 阻塞(D 状态)
# 找 D 状态进程
ps aux | grep ' D '
D 状态(Uninterruptible Sleep)的进程在等待磁盘 I/O 完成,内核这时候不接受任何信号。等磁盘操作完成,进程回到正常状态就杀得掉了。
我之前遇到过 NFS 服务器挂了,客户端有个进程卡在 D 状态半个小时,kill -9 完全没用。最后只能重启机器。
#### 3. 权限不够
# 普通用户杀 root 的进程
ubuntu$ kill -9 1234
# Operation not permitted
普通用户只能杀自己的进程。跨用户杀需要 root 权限:
# 加 sudo
sudo kill -9 1234
#### 4. 进程把自己变成孤儿(daemon 行为)
# 确认 PID 是否变化
ps aux | grep nginx
有些守护进程 fork 之后会重新设置进程组,根进程死了,但它的子进程成了孤儿进程并被 init 收养。你杀的那个可能早就不是原来的 PID 了。
#### 5. 被内核模块保护
极少数安全模块、内核钩子会拦截信号。这种只能通过卸载模块或者重启系统解决。
pkill 和 killall
这两个命令不需要提前查 PID,直接按名字杀:
# 杀掉所有 nginx 进程
pkill nginx
killall nginx
pkill:比 killall 更灵活,支持模糊匹配、正则、用户过滤:
# 杀掉所有 deploy 用户的进程
pkill -u deploy
# 杀掉名字以 "php" 开头的所有进程
pkill -f '^php.*'
# 先匹配再杀,安全点
pgrep -u deploy -l # 列出,确认没问题
pkill -u deploy # 杀掉
killall:名字必须精确匹配(默认情况下,有大小写问题):
# 杀所有叫 nginx 的进程
killall nginx
注意:killall 在 Windows 和 Linux 上含义不同,Windows 上是杀掉所有进程,Linux 上按名字杀。
# 精确匹配名字(避免误杀类似名字的进程)
killall -e nginx
# 先问再杀(交互模式)
killall -i nginx
根据端口杀进程
# 先查端口对应的 PID
lsof -i :8080
# 或者
ss -tlnp | grep :8080
# 查到 PID 后 kill
kill -15 1234
一组全部杀掉(用负号)
# 向整个进程组发信号(PID 前面加负号)
kill -9 -1234 # 杀进程组 1234 里所有进程
# 用 ps 树确认进程组
ps axjf | grep 1234
避坑指南
1. 不要盲目 kill -9 服务进程
kill -9 会让程序来不及做清理:
- ·数据库:未完成的事务直接截断,可能导致数据不一致
- ·Web 服务器:当前处理的请求中断,用户看到 502
- ·文件操作:正在写的文件可能损坏
正确做法是先 kill -15,等几秒,不行再 kill -9。
# 优雅停服务
kill -15 1234
# 等 5 秒
sleep 5
# 检查还活着吗
if kill -0 1234 2>/dev/null; then
echo "5秒还不退,强制杀掉"
kill -9 1234
fi
2. kill 进程时注意依赖关系
# 先找进程树
ps -ef --forest
# 或者
pstree -p 1234
杀父进程之前,确认没有子进程在重要操作。或者先杀子进程,再考虑父进程。
3. pkill 比 killall 的模糊匹配更危险
# 这条会杀掉所有名字里包含 "php" 的进程
pkill php
# 可能误杀:php-fpm, phpls, php-cs-fixer 等所有 php 开头的
# 精确匹配名字更安全
killall php-fpm
想先用 pgrep 预览一下再动手:
# 安全两件套:先查后杀
pgrep -l php
pkill php
4. 检查 PID 文件是否存在
# 如果 PID 文件还在但进程已经死了
cat /var/run/nginx.pid
# 返回 1234
# kill 会报错
kill -0 1234
# "No such process"
# 这时候需要清理 PID 文件
# 或者用 systemd 管理,不要手动 kill
systemctl stop nginx
实战场景(重点!结合真实运维场景)
场景一:Nginx 配置改错了,热重载回滚
改了 nginx.conf 之后 reload,结果发现重载失败了:
# 检查当前哪组 worker 在运行
ps aux | grep 'nginx: worker'
# 从上次配置正常到现在,旧的 worker 可能还在
# 用 kill -HUP 重新加载配置
kill -1 $(cat /var/run/nginx.pid)
# 检查状态
nginx -t
# 配置没问题,重载成功
# 如果实在不行,用优雅退出 + 启动
kill -15 $(cat /var/run/nginx.pid)
# 5 秒后启动
sleep 5
nginx
场景二:数据库卡死,优雅处理
MySQL 进程卡住了,但不能直接 -9 怕丢数据:
# 1. 先用 kill -15 普通信号
kill -15 $(cat /var/run/mysqld/mysqld.pid)
# 2. 等 30 秒,看它是否退出
sleep 30
# 3. 检查是否还活着
if kill -0 $(cat /var/run/mysqld/mysqld.pid) 2>/dev/null; then
echo "MySQL 没响应 SIGTERM,试试 SIGQUIT(core dump)"
kill -3 $(cat /var/run/mysqld/mysqld.pid)
sleep 10
# 4. 还在的话就只能动粗了
if kill -0 $(cat /var/run/mysqld/mysqld.pid) 2>/dev/null; then
echo "最后手段:kill -9"
kill -9 $(cat /var/run/mysqld/mysqld.pid)
fi
fi
实际经验:MySQL 在应对 kill -15 时,会回滚正在进行的事务然后退出。一般 5-10 秒就够了。如果超过 1 分钟还没反应,那可能是有大事务,可以考虑 -9,但要做好数据恢复的准备。
场景三:批量杀掉特定用户的所有进程
有同事的 PHP 脚本死循环,他的用户下几十个进程吃满了 CPU:
# 先看看他有多少进程
ps -u deploy -o pid,cmd --no-headers | wc -l
# 确认后批量杀掉
pkill -u deploy
# 或者给他留点面子,只杀 CPU 超过 50% 的
ps -u deploy -o pid,%cpu --sort=-%cpu | awk '$2 > 50 {print $1}' | xargs kill -15
# 如果不小心把 SSH 进程也干掉了(当前连接跟着断)
# 因为 pkill -u deploy 会杀所有 deploy 用户的进程
# 包括 sshd
# 解决办法:排除 sshd
pkill -u deploy -x -v sshd
场景四:脚本里做进程健康检查
写一个简单的健康检查脚本,放在监控系统里用:
#!/bin/bash
# 检查进程是否活着
check_process() {
local pid_file=$1
local proc_name=$2
if [ ! -f "$pid_file" ]; then
echo "ERROR: PID 文件 $pid_file 不存在"
return 2
fi
local pid=$(cat "$pid_file")
if ! kill -0 "$pid" 2>/dev/null; then
echo "ERROR: 进程 $proc_name (PID $pid) 不存活"
return 1
fi
echo "OK: $proc_name (PID $pid) 运行正常"
return 0
}
# 检查 nginx 和 mysql
check_process /var/run/nginx.pid nginx
check_process /var/run/mysqld/mysqld.pid mysql
今日作业(一道小题目)
题目:你的线上 Nginx 某个 worker 进程 CPU 占用高达 98%,你检查后发现这是正常的工作负载(不是攻击),但配置改错了,需要热重载让 Nginx 加载新配置。
要求写出完整流程:
1. 找到 Nginx 主进程 PID 的命令(2 种方式)
2. 给主进程发送什么信号来重载配置
3. 如果重载后某个 worker 还是 CPU 高,怎么优雅结束单个 worker
4. 写出一个"先优雅后强制"的安全杀进程脚本框架(以某个服务为例)
。