
当 Linux 上的应用程序卡死、或者后台命令停止响应时,仅仅关闭窗口往往并不足以解决问题。进程可能仍在运行、继续占用已打开的文件,甚至阻止你再次启动该程序。
Linux 提供了多种手动终止进程的方式:知道进程 ID(PID)时用 kill,知道确切进程名时用 killall,需要按名称、用户或命令模式匹配时用 pkill。
本文将讲解如何在 Linux 中按 PID 或名称终止进程、信号的工作原理,以及何时该使用 SIGTERM、何时该使用 SIGKILL。
普通用户只能终止属于自己的进程;root 用户,或者使用 sudo 执行命令的用户,则可以终止系统范围内的任意进程。本文介绍的命令适用于所有 Linux 发行版。
速查表
下面的速查表汇总了最常用的进程终止命令,建议收藏备查。
| 命令 | 说明 |
|---|---|
kill PID | 按 PID 向进程发送 SIGTERM |
kill -9 PID | 按 PID 强制结束进程 |
kill $(pidof name) | 向某程序的所有 PID 发送 SIGTERM |
kill -0 PID | 检测 PID 是否存在,不发送任何信号 |
kill -STOP PID | 挂起正在运行的进程 |
kill -CONT PID | 恢复被挂起的进程 |
kill %1 | 终止 1 号后台作业 |
killall name | 向所有名称精确匹配的进程发送 SIGTERM |
killall -u user name | 终止指定用户拥有的匹配进程 |
pkill name | 终止匹配名称模式的进程 |
pkill -u user name | 终止指定用户拥有的匹配进程 |
xkill | 选中 X11 窗口并断开其客户端连接 |
理解 kill 信号
kill、killall 和 pkill 都是向指定的进程或进程组发送指定信号。如果未指定信号,默认发送 15(TERM),即优雅退出。
日常最常用到的信号如下:
| 信号 | 编号 | 默认动作 |
|---|---|---|
SIGHUP | 1 | 终止进程。自行安装处理器的守护进程则会改为重载配置。 |
SIGINT | 2 | 中断进程。Ctrl+C 发送的就是该信号。 |
SIGQUIT | 3 | 终止进程并写出 core dump。Ctrl+\ 发送该信号。 |
SIGKILL | 9 | 立即终止进程。仅在万不得已时使用。 |
SIGTERM | 15 | 默认终止进程。程序可捕获它以便在退出前清理。 |
SIGCONT | 18 | 恢复被挂起的进程。 |
SIGSTOP | 19 | 挂起进程,但不结束它。 |
SIGTSTP | 20 | 挂起进程。Ctrl+Z 发送的就是该信号。 |
上表中的信号编号适用于 x86、ARM 以及绝大多数其它架构。部分架构的编号可能不同,因此在需要跨平台的命令和脚本中,请使用 SIGSTOP、STOP 这类符号名称。
只有 SIGKILL 和 SIGSTOP 无法被程序捕获、阻塞或忽略。其它信号都可以由程序自行处理,这正是为什么一个对 SIGTERM 置之不理的进程,最终仍会被 SIGKILL 干掉。
信号可以用三种方式指定:
- 使用数字,例如
-1 - 带
SIG前缀,例如-SIGHUP - 不带
SIG前缀,例如-HUP
使用 -l 选项可以列出所有可用信号:
kill -l # or killall -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
列表后面还有 SIGSYS,以及从 SIGRTMIN 到 SIGRTMAX 的实时信号。这些实时信号没有固定含义,其具体语义由接收它们的应用程序自行定义。
使用 kill 命令
要用 kill 命令终止进程,首先需要找到它的 PID。可以借助 top、ps、pidof、pgrep 等不同命令来获取。
例如,用 ps 查找正在运行的 Firefox 进程:
ps aux | grep firefox
zoe 1771 8.4 4.1 3418844 331420 ? Sl 09:12 2:31 /usr/lib/firefox/firefox zoe 1856 1.2 1.6 2318512 129640 ? Sl 09:12 0:21 /usr/lib/firefox/firefox -contentproc -childID 1 zoe 1963 0.9 1.4 2298104 114228 ? Sl 09:13 0:17 /usr/lib/firefox/firefox -contentproc -childID 2 zoe 3096 0.0 0.0 9040 2432 pts/0 S+ 09:31 0:00 grep --color=auto firefox
第二列就是 PID,也就是要传给 kill 的数字。最后一行是 grep 命令本身——因为搜索关键词出现在它自己的命令行中才被匹配到,直接忽略即可。
如果你的目的是释放某个端口,而不是关掉屏幕上看得见的程序,可以先查出占用该端口的进程并获取其 PID,再执行终止操作。
如果 Firefox 已经无响应,可以用 pidof 命令查出它的进程 ID:
pidof firefox
该命令会输出所有匹配的 Firefox PID:
2551 2514 1963 1856 1771
先使用默认的 TERM 信号,给进程一个干净退出的机会:
kill 2551 2514 1963 1856 1771
如果进程忽略了 TERM,就发送 KILL 信号:
kill -9 2551 2514 1963 1856 1771
SIGKILL 无法被捕获或忽略,因此会立即终止进程。只有在普通的 kill PID 命令无效时才使用它。
如果不必关心具体的 PID 数字,可以直接把 pidof 的输出交给 kill。同样先使用 SIGTERM:
kill $(pidof firefox)
在 kill 执行之前,Shell 会先把 $(pidof firefox) 替换成 PID 列表。如果这些进程忽略了 SIGTERM,就按前面示例的方式加上 -9。
用 kill -0 检测进程是否存在
信号 0 是个特例。kill 会照常执行存在性与权限检查,但并不真正发送任何信号,因此这是一种开销极小的「检测某个 PID 是否还在」的方法:
kill -0 1771
当进程存在且你有权限向它发信号时,该命令不输出任何内容,并以状态码 0 退出。如果 PID 已不存在,则以非 0 状态退出并打印错误:
bash: kill: (1771) - No such process
这个技巧主要用在脚本里——在脚本中判断退出状态,而不是去解析输出信息:
if kill -0 "$pid" 2>/dev/null; then echo "Process $pid is still running" else echo "Process $pid has exited or cannot be signaled" fi
退出状态为 0 并不能证明原程序还在正常工作。僵尸进程会一直保留它的 PID,直到父进程读取其退出状态;而 PID 一旦释放,内核就可能回收复用。因此长时间运行的脚本,最后检测到的有可能完全是另一个进程,而不是它最初启动的那个。
用 kill -3 发送 SIGQUIT
信号 3(SIGQUIT)会像 SIGTERM 一样终止进程,但它还会请求内核写出一个 core dump——也就是进程内存的快照,供调试器事后分析:
kill -3 1771
core dump 文件是否真的生成取决于系统配置。首先 core 文件大小限制必须允许,可用 ulimit -c 查看;而文件的输出位置由 kernel.core_pattern 决定:
cat /proc/sys/kernel/core_pattern
大多数桌面发行版会把 core dump 通过管道交给崩溃处理程序(crash handler),而不会在程序旁边生成 core 文件,所以在四处找文件之前,先看看这个值的设置。
程序完全可以自行安装 SIGQUIT 处理器,最典型的例子就是 Java 虚拟机。向 Java 进程发送 SIGQUIT,会让 HotSpot VM 把完整的线程转储(thread dump,包括它检测到的所有死锁)打印到该进程的标准输出,而应用程序之后仍会继续运行:
kill -3 $(pidof java)
正是由于这种行为,kill -3 才会频繁出现在 Java 排障笔记中。在启动该进程的终端里按下 Ctrl+\,发送的也是同一个信号。
使用 killall 命令
killall 命令会终止所有匹配指定名称的进程。当某个程序有多个运行实例、而你又不想逐个复制 PID 时,这个命令就非常实用。
沿用前面的场景,向所有 Firefox 进程发送 SIGTERM:
killall firefox
如果 Firefox 仍然没有关闭,就强制结束它:
killall -9 firefox
killall 支持多个选项,例如只向特定用户拥有的进程发送信号、用正则表达式匹配进程名、以及按进程运行时长进行过滤等。在终端中输入 killall --help 可以查看全部选项。
例如,要终止属于用户 zoe 的 Firefox 进程,执行:
sudo killall -u zoe firefox
使用 pkill 命令
pkill 会终止与命令行中给定模式相匹配的进程:
pkill firefox
进程名不必完全匹配,部分匹配同样有效——这让 pkill 更灵活,但也更容易误伤。
在套用宽泛的模式之前,先用 pgrep 预览一下会匹配到哪些进程:
pgrep -a firefox
1771 /usr/lib/firefox/firefox 1856 /usr/lib/firefox/firefox -contentproc -childID 1 1963 /usr/lib/firefox/firefox -contentproc -childID 2
pgrep -a 会打印出所有会被 pkill 用同一模式命中的进程的 PID 及完整命令行。如果这里出现了你没预料到的行,请先收窄匹配模式,再运行 pkill。
如果只想终止用户 zoe 拥有的 Firefox 进程,执行:
pkill -u zoe firefox
如果进程仍未停止,加上 -9:
pkill -9 -u zoe firefox
挂起与恢复进程
终止进程并不总是你想要的结果。如果某个备份任务正在占满磁盘 I/O,或者一次编译正在和更紧急的任务抢资源,你可以先把它暂停,稍后再继续。
SIGSTOP 会挂起进程,但不会结束它:
kill -STOP 1771
被挂起的进程仍留在进程表中,保留自己的内存和已打开的文件,但不再消耗 CPU。ps 会将其状态显示为 T:
ps -o pid,stat,cmd -p 1771
PID STAT CMD 1771 T /usr/lib/firefox/firefox
想让它从中断处继续运行时,发送 SIGCONT:
kill -CONT 1771
与 SIGKILL 一样,SIGSTOP 也无法被捕获或忽略,因此程序既无法拒绝,也没有机会先做收尾清理。如果一个进程正持有锁或打开的网络连接,尽量不要让它长时间处于挂起状态——所有等待它的一方都会一直被阻塞。
当前 Shell 中启动的作业根本不需要 PID。按 Ctrl+Z 即可挂起前台作业(这会发送 SIGTSTP),然后列出被暂停的作业:
jobs
[1]+ Stopped tar -czf backup.tar.gz /home/zoe
用 fg %1 在前台恢复该作业,或用 bg %1 在后台恢复。同样的 % 记法也适用于 kill:kill %1 会向 1 号作业发送 SIGTERM,kill -9 %1 则强制结束它。关于作业的启动与管理,我们在「后台运行 Linux 命令」一文中有更详细的介绍。
使用 xkill 关闭图形界面程序
如果某个 X11 或 XWayland 应用程序无响应,可以使用 xkill:点击该应用窗口,让 X server 断开与之的连接:
xkill
执行命令后,光标会变成一个「X」。点击你想要关闭的窗口即可。断开与 X server 的连接通常会导致应用程序退出,但 xkill 并不会直接终止该进程。
常见问题排查
进程用 -9 也杀不掉 — 如果进程连 SIGKILL(-9)都无法终止,那它很可能是僵尸进程。僵尸进程实际上已经死亡,只是由于其父进程尚未读取它的退出状态,才仍然出现在进程表中。
查找僵尸进程:
ps aux | awk '$8 ~ /Z/'
僵尸进程无法被直接杀死。解决办法是终止它的父进程,或者重启系统。
SIGKILL 无效且进程状态显示为 D — D 表示不可中断睡眠(uninterruptible sleep),通常意味着进程正阻塞在某个内核调用中,等待存储设备或网络文件系统。在该状态解除之前,信号不会被投递,因此 kill -9 只是被排队,而非被忽略。先检查状态:
ps -o pid,stat,cmd -p 1771
请排查底层的 I/O 问题,例如挂死的 NFS 挂载或故障磁盘。如果该状态始终无法解除,重启是唯一的解决办法。
刚杀掉进程它又自动重启 — 对于由 systemd 管理的服务,如果其 unit 文件设置了 Restart=,那么服务退出后会被自动重启。杀死 PID 只会触发这次重启,正确做法是停止该服务:
sudo systemctl stop nginx
提示 Permission denied(权限不足) — 如果出现「Permission denied」错误,说明该进程属于其他用户。请使用 sudo 来终止它:
sudo kill PID
如果进程仍然没有停止,使用 sudo kill -9 PID。
提示 No such process(没有该进程) — 说明该 PID 已经退出。可用 ps -p 1771 确认,或用 pgrep -a firefox 重新查询当前的 PID。还有一种常见情况:在你查到 PID 到执行 kill 之间的这段时间里,程序已经以新的 PID 重启了。
常见问题
kill、killall 和 pkill 有什么区别? kill 接受进程 ID;killall 接受确切的进程名,并向所有以该名称运行的进程发送信号;pkill 则把参数当作正则表达式去匹配进程名,且匹配不要求从开头锚定,因此 pkill fire 会命中 firefox,同时也会命中名称中包含这些字母的其它进程。
如何杀掉某个用户的所有进程? 执行 sudo pkill -TERM -u zoe '.*',即可向 zoe 拥有的所有进程(包括其登录 Shell)发送信号。其中 .* 会匹配所有进程名。只有在进程没有自行退出时,才追加 -9。
kill -9 会导致未保存的数据丢失吗? 有可能。SIGKILL 不会给程序任何机会去刷新缓冲区、保存已打开的文件或清理临时文件。请先发送默认的 SIGTERM,并等待几秒,确认无效后再升级手段。
小结
在 Linux 中终止进程:知道进程 ID 时用 kill PID,知道确切进程名时用 killall name,需要灵活匹配时用 pkill pattern。一律先使用默认的 SIGTERM 信号,只有在进程拒绝退出时才使用 SIGKILL。