资讯详情

Ubuntu下kill -9、kill -15与SIGTERM信号详解:进程管理的正确姿势

📅 2026/9/29 14:00:29 | 华诺云谱 👁 阅读
Ubuntu下kill -9、kill -15与SIGTERM信号详解:进程管理的正确姿势
很多人学 Linux 命令的时候最先记住的杀进程操作就是kill -9觉得 9 是最高级别的信号一杀一个准。我也一样刚用 Ubuntu 那会儿进程卡了就是ps找 PID然后kill -9一顿操作。直到有一次我负责的一个 Java 服务被同事用kill -9强杀之后重启时数据恢复花了大半天我才真正意识到kill -9是一把双刃剑用不好是要出事的。这篇文章我就把 Ubuntu 下杀进程这件事从头到尾讲透包括进程怎么定位、信号怎么选、kill -2、kill -9和kill -15到底有什么区别、为什么有些进程怎么杀都不死以及我在实际环境中踩过的各种坑。复杂的原理我会尽量用大白话讲毕竟我们大多数人是真刀真枪在服务器上干活的不是写内核论文的。1. 找对进程再动手ps / pgrep / top 搭配使用1.1 ps 命令先看懂这些列再考虑杀杀进程的第一原则是先确认你要杀的到底是谁。很多人习惯一条kill -9 $(pgrep java)一把梭结果把同事正在跑的重要任务也一起送走了这种事故我见得太多了。ps是查看进程最常用的命令两个经典姿势是ps -ef和ps aux。ps -ef输出比较紧凑适合快速看进程关系ps aux会额外显示 CPU、内存占用率排查资源占用问题时我更常用后者。实际输入ps aux | head -5输出大概长这样USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.2 168616 11400 ? Ss 12:01 0:15 /sbin/init splash root 2 0.0 0.0 0 0 ? S 12:01 0:00 [kthreadd] root 3 0.0 0.0 0 0 ? I 12:01 0:00 [rcu_gp] www-data 1234 0.3 1.2 345160 61500 ? S 12:05 0:20 php-fpm: pool www新手只需要盯住这几列PID进程号杀进程就是杀这一列的数字。PPID父进程号后面杀进程树的时候会用到。STAT进程状态。S是睡眠R是运行Z是僵尸D是不可中断睡眠T是暂停。%CPU / %MEM资源占用排查卡死时最先看这两列。COMMAND启动命令能看出这个进程到底在跑什么。很多新手只看 PID 就去 kill结果发现进程杀完又自动冒出来一个这是因为真正的“大佬”是 PPID 那列的父进程你把子进程杀了父进程一抬手又拉起一个。这种情况后面会单独讲。1.2 pgrep一秒定位目标进程ps aux输出太密平时我更喜欢用pgrep来快速定位比如要找 nginx 的进程pgrep -a nginx输出会直接给出 PID 和进程名12345 nginx: master process /usr/sbin/nginx 12346 nginx: worker process-a参数很关键它会顺带打印命令行避免只给一串 PID 你还得回头验证是不是目标进程。如果怕 PID 被系统复用kill 之前最好再用一次ps -p PID -o pid,stat,cmd确认。1.3 top / htop实时看谁在吃资源top适合“现场抓人”。机器卡到没响应时执行top后按P按 CPU 排序按M按内存排序谁在搞事一眼就能看到。确认 PID 之后按q退出再使用 kill也可以在 top 界面里直接按k输入 PID 后选择信号发送默认就是 15。htop是 top 的增强版Ubuntu 下如果没装先sudo apt install htop。它能鼠标点选进程、按 F9 弹出信号菜单我在桌面环境排查问题时基本都是用 htop效率比纯键盘操作高不少。2. kill 的底层逻辑它其实只是“发信号”的命令2.1 内核的信号机制很多人对kill有个误解以为它是“杀死进程”的命令。其实kill的真正作用只有一个向指定进程发送一个信号signal。至于进程收到信号之后是死是活、是重新加载配置还是暂停运行取决于信号类型和进程自己的处理逻辑。可以把信号理解成“敲门”。快递员按门铃发信号屋里的人进程听到门铃后可以开门取件也可以装没听见还可以探出头问一句“啥事”。至于最后是签收还是拒收由屋里的人决定。kill -9则属于暴力破门——不管屋里人在干什么直接把门拆了。Linux 内核为每个进程维护了一份信号处理状态信号从内核到进程的传递过程大概是进程执行系统调用或者被调度回用户态时内核发现它有挂起的信号就会触发对应的处理逻辑。如果进程正在深度睡眠、根本不回到用户态那么信号就只能在队列里干等着。2.2 默认动作不等于真实效果每种信号都有一个“默认动作”比如终止进程、忽略、停止、继续等。但进程可以通过系统调用注册自己的信号处理函数。以 Java 为例JVM 收到SIGTERM后通常不是立刻退出而是触发 ShutdownHook执行 Spring 容器关闭、数据库连接池释放等逻辑后再退出。这就是为什么kill -15被称为“优雅停机”。换句话说你kill -15发出去进程不一定会马上死它可能正在做收尾工作。反过来即使你kill -9发出去遇到 D 状态的内核等待进程信号也可能暂时派发不进去。这些细节才是实际运维中最容易碰到的坑。2.3 kill 命令的常规用法kill的基本格式很简单kill [信号] PID信号可以写成数字也可以写成名字。比如kill -15 PID等价于kill -SIGTERM PIDkill -9 PID等价于kill -SIGKILL PID。想查看当前系统支持的所有信号执行kill -l另外提醒一下很多命令手册里写的kill %1是用于操作 shell 任务编号的和直接操作 PID 是两回事不要在脚本里乱套。3. 信号选择kill -2、-15、-9 的差异与适用场景3.1 kill -15SIGTERM给进程一次体面退场的机会SIGTERM编号 15是kill命令默认发送的信号也是日常操作中最应该优先使用的信号。为什么优先用 15因为它给了进程处理善后的机会。大多数服务端程序都会对SIGTERM做专门处理关闭监听端口、保存当前进度、等待正在执行的任务结束、释放锁和临时文件。一个写得规范的服务收到SIGTERM之后能干干净净地退出不会留下脏数据。举个例子我之前维护的一个 Python 爬虫程序退出前需要把 Redis 队列里的任务重新放回去避免任务丢失。代码里用signal.signal(signal.SIGTERM, handler)注册了清理函数。如果用kill -9杀掉Redis 任务直接丢重新跑时要花很长时间补数据。所以在生产环境我处理一个“响应异常但进程还活着”的程序时第一选择永远是kill -15 PID发送之后等几秒再用ps看看进程是否真的退出了。如果 5 到 10 秒内还没退出再考虑升级手段。3.2 kill -9SIGKILL救急用的最后手段SIGKILL编号 9是内核直接执行的强制终止。它的特点是进程无法捕获、无法阻塞、无法忽略。进程收到这个信号后不会有机会执行任何清理代码内核直接回收它的资源。那既然 9 这么干净利落为什么不能当默认方案因为“没有清理”意味着可能出现这些后果数据库进程被强杀未落盘的事务可能丢失甚至导致数据文件损坏进程持有的文件锁不会释放重启后可能报锁冲突进程正在写配置文件写到一半被杀文件内容可能是残缺的依赖优雅退出机制的应用如 ZooKeeper、Elasticsearch直接丢元数据。所以kill -9只适合这些场景进程彻底无响应、kill -15等了几分钟都没动静、或者进程卡死导致系统资源被吃满必须立刻止损。我在生产环境处理“肚脐眼”问题中文环境下大家爱这么调侃的疑难杂症时的原则很简单能 15 就不 99 是兜底方案不是首选方案。3.3 kill -2SIGINT模拟一次 CtrlCSIGINT编号 2就是你在终端按下CtrlC时发送给前台进程组的信号。kill -2 PID等于主动给进程发一个和CtrlC相同类型的信号。SIGINT和SIGTERM很像都是可以被捕获的。区别主要在语义上SIGINT表示“用户主动中断”很多交互式程序会响应它比如 Python 解释器收到后抛KeyboardInterrupt你可以用异常处理做清理SIGTERM表示“系统请求终止”更常用于后台守护进程。有个细节需要注意CtrlC是发送给前台进程组的而kill -2 PID只发送给指定进程。所以如果你启动了一个多进程程序在终端CtrlC可能全部中断但kill -2只能叫停其中之一。3.4 三个核心信号对比为了方便记忆我把最常用的三个信号整理成一张表信号编号默认动作能否被捕获/忽略典型触发方式适用场景SIGINT2终止进程可以CtrlC 或 kill -2中断交互式程序可做清理SIGTERM15终止进程可以kill 或 kill -15优雅停止服务首选方案SIGKILL9终止进程不可以kill -9强杀无响应进程最后手段记住这条链路就够了先 -15等 5 秒没反应再 -9。绝大多数情况下-15 就能解决-9 只是给那些油盐不进、装死到底的程序准备的。3.5 其他几个常用信号除了 2、9、15实际工作中还经常碰到这些信号SIGHUP1终端挂断信号。后台服务常利用它重新加载配置比如 nginx 的 master 进程收到 HUP 会 reload很多运维脚本里kill -1 PID就是这个用途并不是杀人。SIGQUIT3默认终止进程并生成 core dump按下Ctrl\会触发。JVM 收到 SIGQUIT 会打印线程 dump 到标准输出排查线程问题很好用。SIGSTOP19暂停进程无法被捕获或忽略类似“按了暂停键”。可以用kill -STOP PID挂起一个吃 CPU 的进程。SIGCONT18继续运行一个被暂停的进程和 SIGSTOP 配对使用。有一次我要临时腾出 CPU 资源又不想停掉一个算到一半的任务就用kill -STOP让它先暂停等资源空闲了再kill -CONT继续跑非常灵活。4. 实操场景从排查到结束的完整链路4.1 普通应用假死三步操作桌面环境下 Ubuntu 应用卡死我常用这套组合拳第一步htop或ps aux找到目标进程ps aux | grep -v grep | grep firefox第二步先发SIGTERM给它一个体面退出的机会kill -15 8841第三步等 3 到 5 秒检查进程是否还在ps -p 8841 -o pid,stat,cmd如果输出为空说明进程已经退出如果还在再补一脚kill -9。这里有个小技巧ps -p配合-o只输出关键列比ps aux | grep干净很多还避免了 grep 匹配到自身的干扰。4.2 后端服务优雅停机处理 Java、Python 这类有状态的后端服务时我一般会给它更长的等待时间。Spring Boot 应用在收到SIGTERM后要看配置的 graceful shutdown 超时时间默认通常有 30 秒左右的窗口太早补kill -9反而会中断清理过程。我的建议是先kill -15然后用循环等待方式轮询PID$(pgrep -f my-app.jar | head -n 1) kill -15 $PID for i in $(seq 1 30); do if ! kill -0 $PID 2/dev/null; then echo 进程已退出 exit 0 fi sleep 1 done echo 30秒仍未退出强杀 kill -9 $PIDkill -0是检测进程是否存在的标准方法它不发送任何信号只做存活检查。要注意kill -0返回正常只代表进程存在并且你有权限发信号不代表进程状态健康D 状态进程同样能通过这个检查。4.3 按端口杀进程很多时候我们不知道进程名只看到“端口被占用”。比如启动服务时报Address already in use这时候需要根据端口反查进程。Ubuntu 下先看端口占用sudo ss -lntup | grep :8080输出中能看到类似pid12345的字段。如果习惯用 lsof也可以这样sudo lsof -i:8080拿到 PID 之后按老规矩先 15 后 9。这里有个容易踩的坑ss的输出中如果端口处于LISTEN状态进程一般是服务端主进程如果端口是ESTAB那可能只是一个普通连接杀进程前先确认这个端口到底对应的是什么业务。4.4 一棵进程树怎么杀更干净单杀一个进程经常不够因为你杀掉父进程后子进程可能变成孤儿进程被过继给 init继续留在后台运行。此时需要按进程树处理。先找出目标进程的所有子进程pgrep -P 12345然后逐层杀掉子进程最后杀父进程。懒得写脚本的话pkill 提供了按父进程 ID 杀子进程的功能pkill -P 12345这个命令会杀掉所有父进程为 12345 的子进程但不会动父进程本身。杀完子进程再处理父进程kill -15 12345还有一种更直接的方式利用进程组。如果进程没有脱离会话组可以执行kill -- -12345注意 PID 前面加负号表示发送给整个进程组但这要求 12345 是进程组 leader而且进程组里没有需要保留的兄弟进程。实际使用时要格外谨慎我自己在脚本里基本不用这种方式宁可多写两行也不误伤。5. 为什么有的进程怎么 kill 都不死5.1 僵尸进程Z它其实已经“死”了ps输出里状态为Z、命令名后面带defunct的进程叫做僵尸进程。僵尸进程的本质是子进程已经退出但父进程没有调用wait()系统调用来回收它的退出状态所以内核保留了进程条目等待父进程来领取“死亡证明”。它不占用 CPU 和内存只占用一个 PID 和进程表项。这种情况你发kill -9也没用因为进程已经没有执行体了信号根本没有接收方。处理僵尸进程的正确姿势是处理它的父进程如果父进程还活着kill 掉父进程或者让父进程正常退出僵尸会被 init/systemd 自动收割如果父进程就是 PID 1理论上 systemd 会定期回收真遇到不回收的算系统 bug只能重启。我之前排查过一个诡异问题Java 应用莫名其妙无法创建新线程。用ps aux一看几千个defunct进程占满了 PID 上限最后定位到是父进程一直没回收子进程把父进程重启后恢复正常。5.2 D 状态进程内核在等 IO信号插不了手比僵尸进程更让人头大的是 D 状态不可中断睡眠。这种进程通常是因为内核态在等待磁盘、NFS 等 IO 完成进程卡在内核函数里无法回到用户态处理信号。典型场景NFS 挂载的远程目录失联任何访问该目录的进程都会进入 D 状态。你执行kill -9命令本身不报错但进程就是不死。因为信号要等到进程返回用户态才能处理而它返回用户态的前提是 IO 完成IO 又等不到响应就卡死了。这种问题只能从根因入手检查 NFS 服务端是否恢复、磁盘是否还在响应、网络存储是否正常。如果实在不行且无法排查重启系统是最后的选择。D 状态出现频率不高但一出现就很疼排查时先用ps -o pid,ppid,stat,cmd -p PID确认状态再顺着cat /proc/PID/stack看内核栈能定位到具体在等哪个内核函数。5.3 内核线程压根不是给你用的ps aux里那些命令行被方括号括起来的进程比如[kthreadd]、[ksoftirqd/0]、[rcu_gp]是内核线程。它们直接运行在内核态不归传统的用户进程管理机制管普通kill命令对它们无效。正常使用中你也不应该去动它们系统负载再高需要处理的是消耗资源的用户进程而不是内核基础组件。真到了要杀内核线程的地步说明系统已经病入膏肓重启比强杀靠谱。5.4 权限不够Operation not permitted非 root 用户尝试杀掉其他用户启动的进程会收到类似这样的错误bash: kill: (1234) - Operation not permitted这属于正常的权限保护。Ubuntu 默认的普通用户只能杀自己拥有的进程想要杀别的用户进程需要sudo kill -15 1234还有一类隐蔽情况即使你是 root对 PID 1systemd执行kill -9也是无效的。内核为了保护 init 进程屏蔽了对 PID 1 的 SIGKILL 处理。别问我是怎么知道的问就是我刚接触 systemd 时试图用kill -9 1给自己加戏结果啥也没发生。5.5 systemd 托管的服务不要直接手动 killUbuntu 从 15.04 开始使用 systemd 管理服务。对于 systemd 管理的服务如果你直接kill -9掉主进程systemd 发现服务异常退出会按照配置自动拉起一个新进程所以你杀完一看进程又活了好像杀了个寂寞。正确做法是sudo systemctl stop 服务名 sudo systemctl restart 服务名如果你的服务确实出了严重问题systemctl stop 也停不下来可以试试sudo systemctl kill --signal9 服务名这个命令虽然也是发 SIGKILL但它是通过 systemd 的 cgroup 管理接口发送的和普通 kill 有本质区别。6. pkill / killall / xkill配合 kill 的几件武器6.1 pkill按名字匹配方便但有误杀风险pkill 其实不是什么新东西它和 pgrep 是同族工具只是从“列出 PID”变成了“按条件发信号”。基本用法pkill -9 -f my-service这里的-f会匹配完整命令行也就是说只要命令行里包含my-service字符串的进程都会被匹配。这个功能好用的同时也危险如果你在一个路径名很长的目录下执行了包含相同关键字的脚本pkill 可能连你自己的 shell 命令都匹配上直接把当前终端干掉我就被这么坑过一次。用 pkill 时的安全守则先用pgrep -a -f 关键字看看会匹配到哪些进程确认无误再执行尽量避免在脚本里用 pkill -f非要用的化关键字尽量写全减少误匹配面不要轻易用pkill -9 -f java这种模糊关键字服务器上那么多 Java 进程会被一锅端。6.2 killall精确匹配进程名的工具和 pkill 的模糊匹配不同killall 默认精确匹配进程名不是完整命令行。比如killall nginx它只会精确匹配命令名为 nginx 的进程不会误伤nginx-worker或者其他包含 nginx 字样的进程。Ubuntu 默认不自带 killall需要先安装sudo apt install psmiscpsmisc 包里还有pstree、fuser等工具都挺实用。killall 也支持信号参数比如killall -15 nginx、killall -9 java不过我不建议这样杀死 java。6.3 xkill图形化点杀如果是在 Ubuntu 桌面环境下某个 GUI 窗口卡住了可以在终端输入xkill鼠标会变成一个叉号点击你要关闭的窗口它会直接给该窗口的进程发信号一般是 SIGKILL。这个方式是 X Window 层面直接断开客户端连接很多半死不活的窗口一点就没了。不过它只对图形客户端有效对后台服务完全没有意义。6.4 工具横向对比工具匹配方式误杀风险适用场景kill精确指定 PID低已知 PID精准处理单个进程pkill按名称或命令行模糊匹配高快速按名字找一批进程killall精确匹配进程名中精确按名字停止同一进程xkill图形界面点选低桌面 GUI 程序卡死7. 写在最后几个实战小经验踩的坑多了以后我现在写脚本杀进程都会带一套“安全缓冲”逻辑。如果需要在脚本里控制程序运行时长优先考虑timeout命令它到了时间会自动发 SIGTERM不响应还能指定发 SIGKILLtimeout --signal15 --kill-after5 10 ./some-service这条命令的意思是10 秒后发 SIGTERM如果 5 秒内还没退出就补发 SIGKILL。一套完整的优雅退出流程一行搞定。另外通过 SSH 远程执行耗时任务时如果要确保终端断开后任务不被 SIGHUP 打死记得用nohup或者tmux。nohup 的核心作用就是让进程忽略 HUP 信号这种情况下你后续再手动 kill 就要主动关注信号选择别一上来就用 9。根据我个人经验kill这个命令真正难的地方不在命令本身而在对信号和进程状态的理解。建议新手拿到一台并不重要的 Ubuntu 测试机多开几个进程尝试用 15、2、9 分别去终止它们再用ps -o stat观察状态变化几次下来你对进程模型的理解会扎实很多。杀进程看起来只是一条命令背后却是整个 Linux 进程管理机制的浓缩值得花点时间研究透彻。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑