多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Linux kill命令详解:从信号机制到-9与-15的实战区别

Linux kill命令详解:从信号机制到-9与-15的实战区别 1. 先搞明白 kill 到底在干什么很多朋友刚开始接触 Linux第一次敲kill -9的时候理解就是“我把这个进程弄死”。这个理解不能说错但它掩盖了 kill 真正的行为逻辑。如果你只停留在“kill 就是把进程干掉”这个层面那遇到进程杀不掉、杀完僵尸还在、服务重启失败之类的问题大概率会一头雾水。我先把结论放这儿kill 不是一个“杀”命令它是一个“发信号”命令。它的工作本质是向指定进程发送一个信号至于这个信号会让进程退出、暂停、继续还是重新读配置完全取决于你发的信号类型和进程自己的处理逻辑。搞清楚这一点再去理解kill -9、kill -15的区别就水到渠成了。这篇文章围绕 Ubuntu 环境来讲但绝大部门内容在所有 Linux 发行版上通用。无论你是刚装好 Ubuntu 双系统开始折腾命令行的新人还是日常要维护 Linux 服务器的运维同学只要你需要管理进程、排查“为什么杀不掉”这篇文章都值得停留几分钟。2. kill 的底层逻辑信号机制想搞懂 kill先得知道进程在 Linux 里是怎么被“管理”的。Linux 里每个运行中的程序都是一个进程系统会给它分配一个唯一的进程号PID。但系统并不会直接去“操作”这个进程而是通过**信号signal**这种软中断机制来通知它该暂停了、该退出了、该重新加载配置了。信号就是一个数字编号每个编号代表一种预定义的事件。进程收到信号以后可以选择默认处理也可以自己注册处理函数来拦截和响应。打一个不严谨但很好懂的比方kill 命令像一个传话员它跑到目标进程门口喊一嗓子“老板让你下班了”至于对方是真下班、还是回一句“我把手头文档存一下就走”那是进程自己的事。2.1 几个必须记住的常用信号Ubuntu 里信号名和编号的对应关系可以用kill -l查看我这里挑最常用的几个讲清楚。信号编号信号名默认行为能否被进程捕获1SIGHUP挂断退出很多守护进程用它重新读取配置可以2SIGINT中断退出等同于按 CtrlC可以9SIGKILL强制终止内核直接回收不可以15SIGTERM请求终止给进程清理机会可以18SIGCONT继续一个被暂停的进程可以19SIGSTOP暂停进程无法被捕获不可以日常打交道最多的就是 2、9、15 这三个。kill后面如果不带数字默认发的就是编号 15也就是 SIGTERM。这一点很多教程不会特意强调但它非常重要。2.2 为什么进程能“装死”信号的可捕获性我一开始学 Linux 的时候特别困惑kill -15明明发过去了进程为什么还在跑后来才明白SIGTERM 是“请求”性质的信号进程完全可以通过 signal handler 自己决定收到 SIGTERM 后做什么。比如一个数据库进程收到 SIGTERM 后可能先停止接收新请求、把内存里的数据刷到磁盘、释放自己的资源然后才正式退出。这个过程做得好的话数据不会丢服务是“优雅停机”。反过来如果一个程序写得比较粗糙或者被某个系统调用卡住了它收到 SIGTERM 后可能根本没有机会去清理那这个信号就被“忽略”了表现就是进程杀不掉。而 SIGKILL9不一样它是内核直接动手从进程调度层面把目标进程终止并回收资源不给进程任何清理的机会也不允许进程拦截这个信号。所以kill -9从不出手则已出手必然管用——只要目标进程还存在并且你有权限。2.3 从“请配合”到“强制性劝退”如果你把 SIGTERM 理解成“请配合一下你自己安排善后”那 SIGKILL 就是“来不及解释了现在就下线”。这两种方式各有适用场景但问题是很多人一上来就喜欢用kill -9就像两个人沟通碰面第一句就是“你被开了收拾东西立刻走”完全不留任何缓冲余地。kill -9的问题在于进程来不及保存状态。如果你在杀一个正在写文件的程序、一个数据库、或者某个正在做长任务的脚本强杀很可能留下半截数据、损坏的索引甚至让某些文件处于不一致状态。我的建议是除非进程已经明确无响应、或者你确定它不涉及任何需要持久化的数据否则都应该先给 SIGTERM给它一个“正常体面地离开”的机会。3. kill -2、kill -15、kill -9 究竟差在哪搜索热词里反复出现“kill -2和kill -9和kill -15区别”可见这是大家最容易搞混的点。我直接把它们放在一起对比用同一个场景来说。假设你现在跑了一个 Python 写的数据处理脚本跑了一半想终止它。你会有三种常见的做法按 CtrlC其实发的就是 SIGINT2进程收到后如果没特殊处理默认终止。这更多是前台交互时的手动操作。如果这个进程在后台跑你想单独给它发一个 2 号信号就可以写kill -2 PID。kill -15 PID是默认的“礼貌”方案请求进程退出。大部分成熟的程序会监听这个信号做清理动作。比如 Nginx 收到它会停止接收新连接然后平滑退出MySQL 收到它会刷脏页、关闭 redo log、然后退出这些都是我实际运维中见过的行为。kill -9 PID就是“粗暴模式”内核强制回收。程序的一切善后逻辑都不执行文件描述符直接被关闭内存直接释放。它的好处是绝对有效坏处是没有善后。括号里那三个数字的大小不代表力度递增只是信号编号不同。信号编号越小并不代表越“温柔”。真正决定行为的是信号本身的语义和进程的处理逻辑。很多人以为 -9 最大所以最狠这个理解方向没错但它不是“编号越大越狠”这么简单。3.1 关键点程序能不能“拦”这个信号再深挖一层。SIGINT 和 SIGTERM 都是可以捕获的。这意味着程序员可以在代码里写一个信号处理函数在退出前保存数据、关闭连接、删除临时文件。很多生产级程序就是这么做的。SIGKILL 则无法被捕获。内核在发出 SIGKILL 的瞬间对这个进程来说就是“审判结束”没有申诉机会。所以处理线上服务或者有状态应用的时候严禁一上来就 kill -9这是很多初学运维最容易踩的坑。我见过有同事在生产环境用 kill -9 杀数据库主进程结果重启之后花了大半天做数据恢复。自那以后我们组内部就定了规矩先 -15观察几秒不行再 -2最后还是不行才上 -9。这条习惯我希望你也能养成。3.2 什么时候必须用 -9听我这么一说你可能觉得 -9 是个坏东西。其实不然它在该出手的时候非常可靠。进程卡在死循环里SIGTERM 完全没反应。进程处于不可中断睡眠状态比如等待某块磁盘 IO 迟迟不返回你发 15 号信号它根本顾不上处理。程序有 bug信号处理函数一直不退出甚至死锁了。你想清理的进程是某个故意屏蔽了 SIGTERM 的恶意进程或异常脚本。这些场景下kill -9是唯一高效的选择。但我强调一点要用它但别滥用它。4. Ubuntu / Linux 下 kill 的实战操作全流程讲完理论现在把袖子撸起来一步步演示在 Ubuntu 上怎么用 kill 杀掉进程。这个流程来自我的日常操作习惯每一步都有明确目的跟着走一遍基本不会出问题。4.1 第一步找到进程 PID选对你自己的工具杀进程的前提是知道进程 PID。有很多种办法我按照使用频率给你盘一下。ps -ef | grep 进程名是最通用的方式。比如我想找到 Nginx 的进程ps -ef | grep nginx输出里每一行都对应一个进程第一列是用户名第二列是 PID第三列是父进程 PID。pgrep 进程名更直接直接输出匹配进程名的 PID适合在脚本里用。pgrep nginxpidof 进程名和 pgrep 类似但它对精确匹配进程名更友好。pidof nginx还有一个藏在日常操作里的技巧用ps -ef | grep 进程名的时候grep 自己也可能会出现在结果里那个带 grep 的 PID 是你不需要管的“干扰项”。新手经常被这个误导以为自己永远杀不掉目标进程。4.2 第二步用 kill 发送不同信号找到 PID 之后随便挑一个来发信号。假设 Nginx 的主进程 PID 是 12345我想让它正常退出就执行kill -15 12345 # 或者 kill 12345因为不带参数默认发 SIGTERM所以上面两条等价。想发 SIGINT 就执行kill -2 12345想强制终止就执行kill -9 12345注意这里-9、-15是信号编号的缩写也可以用信号名比如kill -SIGKILL 12345和kill -9 12345等价。Ubuntu 的kill命令对这两种写法都支持。4.3 第三步确认进程真的退出了这一步最容易被忽略。很多同学执行完 kill 就以为万事大吉结果过一会儿发现服务还在响应或者进程变成了僵尸。发完信号后一定要用前面提到的方式再确认一遍ps -ef | grep nginx pgrep nginx如果没有任何输出说明进程已经终止。如果 PID 还在但状态列变成了Z僵尸进程那就要按后面常见问题里说的方式去处理了。把“发信号”和“确认结果”绑定成一个完整操作这个习惯非常重要几乎所有和进程相关的踩坑事故都跟少做了这步确认有关。4.4 配套命令killall 和 pkill一次搞定多个进程kill是按 PID 针对性操作的但有些情况下你更想“按名字杀”。比如你起了好几个同名 worker 进程一个个查 PID 再逐个 kill效率太低了。pkill 进程名是按名称匹配并发送信号支持模糊匹配。pkill nginx pkill -9 nginxkillall 进程名则是精确匹配完整进程名更保守一点适合确定场景。killall nginx killall -9 nginx这两个命令本质还是在发信号只是省去了手动查 PID 的环节。但有一个隐藏风险pkill 的模糊匹配可能会误杀比如你执行pkill -9 nginx结果匹配到了nginx_old或者别的带 nginx 字样的进程。所以我个人的习惯是在不确定是否会有同名进程时宁可先用pgrep -l 进程名看一下匹配到了哪些 PID再决定用 kill 还是 pkill。4.5 几个实用案例从脚本后台到常见服务杀掉一个后台运行的 nohup 脚本。很多朋友用nohup python3 test.py 启动了脚本后来想停掉它。先pgrep -f test.py找到 PID再kill -15 PID。nohup 启动的进程通常不会自己处理 SIGTERM默认行为就是退出所以直接 kill 就行没必要一上来就kill -9。终止一个卡死的 apt 或 dpkg 进程。Ubuntu 下安装软件时如果中断很容易残留apt或dpkg进程占用锁。这种场景下可以先看日志确认到底是卡死还是正在执行。如果是卡死再用kill -9处理之后记得删除残留的锁文件比如/var/lib/dpkg/lock-frontend再执行dpkg --configure -a修复。让服务重新读取配置文件。比如修改了 Nginx 配置可以不重启进程直接给 master 进程发 SIGHUPkill -1 $(cat /var/run/nginx.pid)这就是信号机制的灵活之处kill 不只是用来“杀”进程还能做这种温和的管理操作。5. 进程杀不掉怎么办常见问题排查实录这一节我整理了自己实操和给朋友排查时最常见的几个问题。每种情况都附上原因分析和排查思路你对照着场景找处理办法就行。5.1 Permission denied权限不够怎么处理普通用户只能向自己拥有的进程发信号。你kill一个别的用户比如 root启动的进程系统会直接拒绝提示Operation not permitted。解决方法很简单使用 sudo 提权sudo kill -15 12345但我要提醒一句sudo 是一把双刃剑。你有了 root 权限就能杀任何进程但也意味着你可能误杀系统关键进程。所以在敲 sudo kill 之前先确认 PID 是不是真的对了、是不是真的要杀。我见过有人 sudo kill -9 的时候少打了一个数字把 sshd 或者 desktop 的关键进程给杀了整台机器直接“盲盒”处置。小技巧如果提示No such process但你觉得这个进程明明存在先确认 PID 是否已经变化。有些程序自带守护/自动拉起机制老进程被杀了新进程马上被拉起PID 已经变更。5.2 僵尸进程kill -9 也救不回来的情况这是新手最容易懵的场景。你用ps -ef看目标进程状态是Z然后kill -9 PID结果系统告诉你操作成功但进程还在列表里躺着一动不动。原因在于僵尸进程本身已经不是“活”的了。它的所有资源已经被内核回收只是在进程表里保留了一个条目等着父进程来确认它的退出状态。所以严格来说僵尸进程不需要也无法被杀死它已经不是运行中的程序只是一个残留的“墓碑记录”。真正的解决办法有两个方向。一是处理它的父进程。僵尸进程的父进程如果还活着要么让父进程调用 wait 来回收这个记录要么直接终止父进程让 PID 1init/systemd接管重新检查并清理子进程记录。二是如果父进程一直不回收而且这个父子结构是顽固的那你需要找到并处理整个父子链。ps -ef | grep defunct看到 defunct 字样的就是僵尸。反查一下它的 PPID父进程 PID再做进一步处理。这块的独家体会是不要花大力气去杀成了僵尸的进程那是一条死路。正确的思路是去处理“制造僵尸”的父进程只要父进程被清理掉僵尸条目通常很快会被系统回收。5.3 服务总是杀不掉后台进程与守护进程很多服务自带守护逻辑你 kill 掉主进程守护进程立刻又拉起新的进程。表现就是杀了一次又一次进程还是在那。遇到这种情况建议先查清进程间的父子关系。用ps -ef查看输出里的 PPID 列找到真正的“元凶”父进程通常是服务的管理进程或守护进程。把父进程处理掉子进程才会消停。如果是 systemd 管理的服务用systemctl stop 服务名来优雅停止而不是直接 kill 一个没头没脑的 PID反而更可靠。另外还有一个常见场景——你启动了某个程序想在 SSH 断开后让它还跑着于是用了nohup或者把它放到了后台。现在你想停掉整个进程组光 kill 主进程可能不够因为子进程还活着。可以用ps -ef | grep 进程名把相关进程全找出来或者用前面说的 pkill 按进程名清理。如果还是搞不定可以查一下进程树pstree -p PID这样你能一眼看清谁是谁的子进程就不会跟无头苍蝇一样乱杀了。5.4 环境出问题时如何用 kill 给自己留一条退路热词里“ubuntu环境变量配置错误”“xshell连接ubuntu网络配置”这些本质上跟 kill 关系不大但我在实际给朋友远程排查时确实遇到过因为改坏了环境变量导致连ls、vim这些命令都用不了的情况最后是靠着残留的会话和绝对路径的 kill 才把多余进程清理掉、恢复系统。举一个真实例子。某次我在一台 Ubuntu 上给用户配置 JAVA_HOME手滑把 PATH 覆盖了保存完发现连sudo、ls都提示 command not found。但终端会话没关我还有机会补救。这里有两个关键点一是用绝对路径调用命令。很多命令在/usr/bin或者/bin下比如 kill 在/bin/kill所以环境变量再乱/bin/kill依然能直接用/bin/kill -15 12345二是如果你还有控制台的窗口可以考虑直接重启系统或者通过另一条干净的会话把 .bashrc 改回来。但如果你现在唯一能控制的就是一个会话那就靠着绝对路径先把不需要的进程清理掉再想办法恢复文件。这个经验虽然看起来有点极端但它告诉了我一个道理会精确地使用 kill在关键时刻真的能救急。5.5 容易忽略的 D 状态进程ps -ef里还有一种状态叫D全称是 uninterruptible sleep不可中断睡眠。这种进程通常在等待内核态资源返回比如正在读写网络文件系统、磁盘卡住、内核模块在处理请求。此时你发 SIGTERM 它不响应发 SIGKILL 它也可能暂时不退出。遇到 D 状态进程先别急着反复操作。关键要看它卡在什么 IO 上。如果磁盘损坏或者网络挂载出问题导致无响应治本的方法是恢复背后的存储或网络资源资源恢复了进程自然能退出。如果等太久仍然不退出那么最后一次手段通常需要重启系统因为这类进程从用户态已经无法强制消除。这时候去反复kill -9不但没用还可能让你忽略真正的问题根源。学会看进程状态列是进程管理的必修课D 状态就是最典型的一个坑。6. 我处理进程问题时的一点个人习惯最后给你分享几条我自己摸索出来的实操习惯不一定每条都适合你但大概率能让你少踩几个坑。先查后杀永远先确认 PID。不管是对本地 Ubuntu 还是远程服务器我从不凭空敲 kill先ps -ef或pgrep拿到精确的 PID并确认它不是系统关键进程。这个动作多花十秒钟但能省掉一晚上的麻烦。信号力度从小到大。先 SIGTERM等三五秒不行再 SIGINT最后才 SIGKILL。这个顺序不是讲客气是在给你自己和程序留余地。程序如果真的支持优雅退出你给它机会它会主动保存状态并干净退出。尤其生产环境任何一次数据和状态不一致都可能造成大麻烦。善用命令历史与 PID 文件。有些服务启动后会把自己主进程的 PID 写到/run/或者/var/run/下的 pid 文件里比如/var/run/nginx.pid。读这个文件再去 kill比用 grep 匹配进程名更准确。kill -15 $(cat /var/run/nginx.pid)多了解 systemd。现代 Ubuntu 上大量服务由 systemd 管理这类服务更推荐用systemctl stop/restart而不是直接 kill因为 systemd 会额外处理依赖关系和状态跟踪。kill 更像是底层手术刀systemctl 则是专业服务管理工具两者互相配合才顺手。我在实际使用中还有一个体会管理进程的心态有点像收拾一个热闹的办公室。你先通知大家“准备下班了”大部分人听到后会把桌面收拾好、把电源关了再走只有少数人耳机戴得太专注根本没听见这时候才需要走到他跟前拍拍肩膀。kill 命令就是那个通知机制-9 就是最后被逼无奈才动用的“拍肩膀”。把握好这个度你就真正理解了 Linux 的进程管理艺术。
返回列表