当前位置:首页>Linux>每天学一个Linux命令系列(25):kill - kill -9是下策,先试试这些信号

每天学一个Linux命令系列(25):kill - kill -9是下策,先试试这些信号

  • 2026-09-29 16:28:44
每天学一个Linux命令系列(25):kill - kill -9是下策,先试试这些信号
运
运维少年 · 科技观察

这个命令是干啥的

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 个就够日常用了):

编号名称用途
1SIGHUP重载配置 / 终端断线
2SIGINTCtrl+C 中断
3SIGQUITCtrl+\ 退出(含 core dump)
9SIGKILL强制杀掉(不能忽略)
15SIGTERM优雅结束(默认信号)
18SIGCONT继续被暂停的进程
19SIGSTOP暂停进程(不能忽略)
20SIGTSTPCtrl+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. 写出一个"先优雅后强制"的安全杀进程脚本框架(以某个服务为例)

。

E N D

运维少年 · 科技观察

最新文章

随机文章