killall 命令详解与参数实测(Ubuntu 22.04)
killall 按精确进程名批量终止:本文在 Ubuntu 22.04(psmisc 23.4)逐参数实测,覆盖默认 TERM、-s/-9/-SIGKILL 信号形式、-i 交互、-w 等待退出、-u 按用户、-r 正则、-I 忽略大小写、-y/-o 时间过滤、-g 进程组、-n namespace,重点剖析长名 15 字符截断与 -e 实测真相及 pkill 差异。
killall 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 killall 的每一个常用参数——按精确进程名批量终止、默认 TERM 与
-s/-9/-SIGKILL三种信号形式、-i逐个交互确认、-w等待进程真正退出、-u按用户过滤、-r扩展正则、-I忽略大小写、-y/-o按启动时间过滤、-g杀整个进程组、-n匹配同 namespace、-Z按 security context 匹配,以及 15 字符长名截断匹配与-e的实测真相,全部命令与回显已在 Ubuntu 22.04(psmisc 23.4)实测通过。
一、环境信息
本节记录实测环境与演示数据,便于读者对照复现。killall 是 psmisc 软件包的核心工具之一,与 pgrep/pkill 同属"按名字找进程"的家族,但匹配哲学完全不同:killall 只做精确进程名匹配,pkill 按正则模式匹配——这是全文反复出现的核心差异。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- killall 版本:psmisc 23.4(软件包
psmisc 23.4-2build3,见 2.3 实测) - Shell:GNU bash 5.1.16(1)-release,root 身份执行
- 演示目录:
/root/demo/killall/ - 测试用户:普通用户
tester(用于-u按用户实验) - 演示进程:改名副本
myworker(cp /usr/bin/sleep /root/demo/killall/myworker)、大写名BigWorker、长名this-is-a-long-cmdname-x/-X/-Y、改名 bashpgworker/pg2(进程组实验)、trap 工作脚本trapworker
二、命令概览
2.1 语法与测试数据准备
killall 的语法是 killall [OPTION]... [--] NAME...:按精确进程名匹配进程并向每个命中进程发信号,不指定信号时默认发 SIGTERM。与 pkill 按模式匹配不同,killall 的 NAME 是字面进程名(comm 或完整名),不是正则——想要正则能力必须显式加 -r。无匹配时打印 NAME: no process found 并返回退出码 1。
准备演示数据:把 /usr/bin/sleep 复制成三个不同名字的可执行文件(进程名跟随可执行文件名),并启动 3 个 myworker:
cp -f /usr/bin/sleep /root/demo/killall/myworker; cp -f /usr/bin/sleep /root/demo/killall/BigWorker; cp -f /usr/bin/sleep /root/demo/killall/this-is-a-long-cmdname-x
/root/demo/killall/myworker 300 & /root/demo/killall/myworker 300 & /root/demo/killall/myworker 300 & echo three-myworker-started
用 ps 对账确认演示进程就位:
root@ubuntu2204:~# /root/demo/killall/myworker 300 & /root/demo/killall/myworker 300 & /root/demo/killall/myworker 300 & echo three-myworker-started
three-myworker-started
root@ubuntu2204:~# sleep 0.3; ps -eo pid,comm,args | grep -E '[m]yworker|[B]igWorker|[t]his-is'
438 myworker /root/demo/killall/myworker 300
439 myworker /root/demo/killall/myworker 300
440 myworker /root/demo/killall/myworker 300
3 个 myworker(comm 列同名)就位。ps 的 args 列显示完整命令行路径,comm 列显示进程名——killall 默认匹配的正是 comm。
💡 技术拓展:按名字管理进程的同类工具不止 killall 一个,横向对比——
工具 所属包 匹配方式 动作 典型用途 kill shell 内置/procps 按 PID 发信号 已知 PID 精确打击 killall psmisc 精确进程名(字面) 发信号 按名字批量终止 pkill procps-ng 扩展正则(comm/ -f全命令行)发信号 模式化批量终止 pgrep procps-ng 扩展正则(同 pkill) 打印 PID 查找进程 pidof sysvinit-utils 精确程序名 打印 PID 服务脚本取 PID 本文主角是 killall:匹配最"死板"(字面精确名),也因此最可控——不会像 pkill 那样被正则元字符意外命中。本文实际采用 killall 做按名终止,并用 pkill 做对照实验。
2.2 --help 全量输出(逐行照抄)
killall 的选项集不大,先看全貌——--help 实测输出(逐行照抄,psmisc 23.4):
root@ubuntu2204:~# killall --help
Usage: killall [OPTION]... [--] NAME...
killall -l, --list
killall -V, --version
-e,--exact require exact match for very long names
-I,--ignore-case case insensitive process name match
-g,--process-group kill process group instead of process
-y,--younger-than kill processes younger than TIME
-o,--older-than kill processes older than TIME
-i,--interactive ask for confirmation before killing
-l,--list list all known signal names
-q,--quiet don't print complaints
-r,--regexp interpret NAME as an extended regular expression
-s,--signal SIGNAL send this signal instead of SIGTERM
-u,--user USER kill only process(es) running as USER
-v,--verbose report if the signal was successfully sent
-V,--version display version information
-w,--wait wait for processes to die
-n,--ns PID match processes that belong to the same namespaces
as PID
-Z,--context REGEXP kill only process(es) having context
(must precede other arguments)
头部三行给出三种合法形态(带选项杀名 / -l 列信号 / -V 版本),随后 17 个选项就是第三章逐项实测的对象。注意 -Z 帮助文本里的括注 "(must precede other arguments)":context 正则必须放在其他参数之前。
2.3 版本信息
确认版本与软件包归属:
root@ubuntu2204:~# killall --version | head -2
killall (PSmisc) 23.4
Copyright (C) 1993-2021 Werner Almesberger and Craig Small
PSmisc comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it under
the terms of the GNU General Public License.
For more information about these matters, see the files named COPYING.
💡 说明:命令里加了
| head -2,但因 killall 一次性把版本文本写入管道缓冲区,head 截断显示前两行后,剩余缓冲内容仍出现在了输出中——这是管道与 SIGPIPE 时序的常见现象,不影响版本结论:killall (PSmisc) 23.4。
再用 dpkg 确认软件包归属与完整包版本:
root@ubuntu2204:~# dpkg -l psmisc | tail -1
ii psmisc 23.4-2build3 amd64 utilities that use the proc file system
killall 属于 psmisc 包("utilities that use the proc file system",同包还有 pstree/fuser),Ubuntu 22.04 最小安装即自带。
三、参数逐项实测
3.1 精确名杀一组:killall NAME
不带任何参数,killall 向所有 comm 等于 NAME 的进程发 SIGTERM。3 个 myworker 一次清空:
root@ubuntu2204:~# killall myworker; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -x myworker || echo all-myworker-dead
all-myworker-dead
退出码 0 表示至少命中并成功发送一个信号,pgrep 复查计数归零。配合 -v 可以看到它到底杀了几号进程:
root@ubuntu2204:~# /root/demo/killall/myworker 300 & sleep 0.3; killall -v myworker; echo verbose_rc=$?
Killed myworker(83) with signal 15
verbose_rc=0
Killed myworker(83) with signal 15:信号 15 即 SIGTERM(默认值),括号内是 PID。
💡 要点:killall 按 comm 精确匹配,
killall myworker绝不会碰到myworker2或wor——这是它与 pkill 的本质区别,3.2 节用对照实验证明。
3.2 部分名不匹配:与 pkill 的核心差异
killall 的 NAME 是字面全名,传入部分名直接报"无进程"——而 pkill 把同样的部分名当正则子串,能匹配并杀死:
root@ubuntu2204:~# /root/demo/killall/myworker 300 & sleep 0.3; killall wor; echo killall_rc=$?
wor: no process found
killall_rc=1
root@ubuntu2204:~# pgrep -x myworker >/dev/null && echo still-alive
still-alive
root@ubuntu2204:~# pkill wor; echo pkill_partial_rc=$?
pkill_partial_rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -x myworker || echo killed-by-pkill-partial
killed-by-pkill-partial
同一个 wor:killall 找不到进程(rc=1,myworker 幸存),pkill 正则匹配成功并把 myworker 杀掉了。结论:killall 更"钝"但更安全,pkill 更灵活但需警惕模式误伤。 想让 killall 拥有模式能力,用 -r(见 3.10)。
3.3 -i:逐个交互确认
杀一组同名进程前逐个询问 Kill NAME(PID) ? (y/N),回答 n 跳过、y 才发信号。预置两个 myworker 后用管道喂入 n、y 两行回答:
root@ubuntu2204:~# /root/demo/killall/myworker 300 & /root/demo/killall/myworker 300 & sleep 0.3; ps -eo pid,comm | grep '[m]yworker'
454 myworker
455 myworker
root@ubuntu2204:~# printf 'n\ny\n' | killall -i myworker 2>&1; echo rc=$?
Kill myworker(454) ? (y/N) Kill myworker(455) ? (y/N) rc=0
root@ubuntu2204:~# sleep 0.3; ps -eo pid,comm | grep '[m]yworker' || echo none-left
454 myworker
回答 n 的 454 号进程被跳过、仍然存活,回答 y 的 455 号被杀。交互提示默认大写 N,直接回车等于不杀。
💡 要点:
-i是生产环境对"同名进程一堆"时最稳的保险——尤其当进程名恰好与系统关键服务同名时,逐个确认能避免一刀切。
3.4 -w:等待进程真正退出
默认 killall 只负责"发信号"不负责"等死";加 -w 后阻塞直到所有目标进程消失,退出码 0 即代表已经死透:
root@ubuntu2204:~# /root/demo/killall/myworker 300 & sleep 0.3; killall -w myworker; echo wait_kill_rc=$?
wait_kill_rc=0
root@ubuntu2204:~# pgrep -x myworker || echo confirmed-dead
confirmed-dead
wait_kill_rc=0 返回时进程已不存在,后续命令(如重启服务)可以放心执行。psmisc 源码里 -w 每秒轮询一次目标进程,配合 -q 可做"静默等死"(见 4.2 实战)。
3.5 -s/--signal 与 -9/-SIGKILL:指定信号
默认发 SIGTERM(15),-s SIGNAL 可换成任意信号,SIGNAL 既可写名字也可写数字;psmisc 23.4 还支持 kill 风格的 -9、-SIGKILL 前缀形式。三种写法实测等效:
root@ubuntu2204:~# cp -f /usr/bin/sleep /root/demo/killall/myworker; cp -f /usr/bin/sleep /tmp/myworker; chmod 755 /tmp/myworker; echo copies-ready
copies-ready
root@ubuntu2204:~# /root/demo/killall/myworker 300 & M1=$!; sleep 0.3; killall -s KILL myworker; echo s_kill_rc=$?; sleep 0.3; kill -0 $M1 2>/dev/null || echo killed-by-KILL
s_kill_rc=0
/root/killall-s2.sh: line 2: 79 Killed /root/demo/killall/myworker 300
killed-by-KILL
-s KILL(SIGKILL/9)生效:bash 报告后台作业 79 Killed(脚本形式带脚本名与行号;交互终端显示为 [1]+ Killed ...)。数字与 -SIGKILL 形式:
root@ubuntu2204:~# killall -q myworker 2>/dev/null; /root/demo/killall/myworker 300 & sleep 0.3; killall -s 9 myworker; echo rc=$?; sleep 0.3; pgrep -x myworker || echo killed-by-s9
/root/killall-x1.sh: line 2: 174 Killed /root/demo/killall/myworker 300
rc=0
killed-by-s9
root@ubuntu2204:~# killall -q myworker 2>/dev/null; /root/demo/killall/myworker 300 & sleep 0.3; killall -9 myworker; echo rc=$?; sleep 0.3; pgrep -x myworker || echo killed-by-9
/root/killall-x1.sh: line 2: 180 Killed /root/demo/killall/myworker 300
rc=0
killed-by-9
root@ubuntu2204:~# killall -q myworker 2>/dev/null; /root/demo/killall/myworker 300 & sleep 0.3; killall -SIGKILL myworker; echo rc=$?; sleep 0.3; pgrep -x myworker || echo killed-by-SIGKILL
rc=0
/root/killall-x1.sh: line 2: 186 Killed /root/demo/killall/myworker 300
killed-by-SIGKILL
-s 9、-9、-SIGKILL 三种形式 rc 全为 0,目标进程全部消失。中间的 Killed 行是 bash 对后台作业被 SIGKILL 的报告(第三条的报告行因作业回收时序错位,出现在 rc=0 之后)。
💡 要点:默认 TERM 可以被进程捕获后优雅退出(见 4.4),KILL 不能。运维惯例是先 TERM 观察再 KILL 兜底,killall 全程一条工具搞定。
3.6 -v:回显所杀
-v(verbose)对每个成功送达的信号打印一行 Killed NAME(PID) with signal N,是审计与调试的利器(3.1 已单进程示例)。多进程场景配合正则一次看清全部目标(详见 4.1):
root@ubuntu2204:~# /root/demo/killall/myworker 300 & /root/demo/killall/myworker 300 & sleep 0.3; killall -v -r 'mywor.*'; echo rc=$?
Killed myworker(115) with signal 15
Killed myworker(116) with signal 15
rc=0
💡 说明:verbose 对每个命中进程各打印一行,PID 各自独立,多目标时正好当"击杀清单"用;
-v -r组合的完整实战见 4.1。
3.7 -q:静默模式与无匹配返回码
无匹配时 killall 会向 stderr 打印 NAME: no process found 并返回 1;-q(quiet)压掉报错文本,退出码语义保留,适合写脚本:
root@ubuntu2204:~# killall zzz-no-such-proc; echo rc=$?
zzz-no-such-proc: no process found
rc=1
root@ubuntu2204:~# killall -q zzz-no-such-proc; echo quiet_rc=$?
quiet_rc=1
带不带 -q,退出码都是 1——差别只在 stderr 那行报错文本。脚本里习惯用 killall -q NAME 2>/dev/null 双保险(-q 管报错文本,重定向兜住其余杂音)。
3.8 -l:列出全部信号名
killall -l 打印内核支持的全部信号名(31 个常规实时信号名):
root@ubuntu2204:~# killall -l
HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT
CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS
输出即 -s 参数可用的信号名清单:第一行 TERM(默认信号)、KILL,第二行 HUP、USR1 等应用常用信号都在。
3.9 -u:只杀指定用户的进程
-u USER 把匹配范围限定在指定用户名(或 UID)名下,其他用户的同名进程不受影响。root 先起一个 myworker 占位,再给 tester 起两个 sleep:
root@ubuntu2204:~# cp -f /usr/bin/sleep /root/demo/killall/myworker; /root/demo/killall/myworker 300 & echo root-myworker-started
root-myworker-started
root@ubuntu2204:~# su tester -c 'sleep 77' & SU1=$!; su tester -c 'sleep 88' & SU2=$!; sleep 0.5; ps -u tester -o pid,user,comm,args
PID USER COMMAND COMMAND
20 tester sleep sleep 77
21 tester sleep sleep 88
root@ubuntu2204:~# killall -u tester sleep; echo rc=$?
Terminated
rc=0
Terminated
root@ubuntu2204:~# kill $SU1 $SU2 2>/dev/null; sleep 1; pgrep -c -u tester -f 'sleep (77|88)' || echo tester-clean
0
tester-clean
root@ubuntu2204:~# pgrep -x myworker >/dev/null && echo root-myworker-still-alive || echo root-myworker-gone
root-myworker-still-alive
killall -u tester sleep 只杀了 tester 的两个 sleep(两行 Terminated 是 su 持有的前台进程收到 TERM 后 su 打印的终止通告),root 自己的 myworker 复查仍然存活——-u 的隔离效果达成。
⚠️ 注意:选项拼写错误会导致整条命令失败。实测把不存在的
-x混进参数(killall -u tester -x sleep),killall 直接打印 Usage 并返回 1,一个进程都没杀:root@ubuntu2204:~# killall -u tester -x sleep; echo rc=$? Usage: killall [OPTION]... [--] NAME... killall -l, --list killall -V, --version -e,--exact require exact match for very long names -I,--ignore-case case insensitive process name match -g,--process-group kill process group instead of process -y,--younger-than kill processes younger than TIME -o,--older-than kill processes older than TIME -i,--interactive ask for confirmation before killing -l,--list list all known signal names -q,--quiet don't print complaints -r,--regexp interpret NAME as an extended regular expression -s,--signal SIGNAL send this signal instead of SIGTERM -u,--user USER kill only process(es) running as USER -v,--verbose report if the signal was successfully sent -V,--version display version information -w,--wait wait for processes to die -n,--ns PID match processes that belong to the same namespaces as PID -Z,--context REGEXP kill only process(es) having context (must precede other arguments) rc=1看到 Usage 即参数有拼写/组合错误——killall 的
-x不存在(-x精确匹配是 pkill 的选项),去掉即可。这也提示一个排障捷径:报错回显里自带完整合法选项表。
3.10 -r:扩展正则表达式匹配
-r 把 NAME 从字面名升级为扩展正则(ERE),恢复"模式匹配"能力。用 mywor.* 杀掉 myworker:
root@ubuntu2204:~# /root/demo/killall/myworker 300 & sleep 0.3; killall -r 'mywor.*'; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -x myworker || echo regex-killed
regex-killed
正则作用于进程名(comm,got_long 时含完整名)。加了 -r 之后 killall 的行为与 pkill 趋同,此时更要小心 . * 等元字符的误伤面(配合 -v 先验证再落刀)。
3.11 -I:忽略大小写匹配
进程名大小写敏感:小写 bigworker 匹配不到 BigWorker;加 -I 后命中:
root@ubuntu2204:~# cp -f /usr/bin/sleep /root/demo/killall/BigWorker; /root/demo/killall/BigWorker 300 & sleep 0.3; ps -eo pid,comm | grep '[B]igWorker'
281 BigWorker
root@ubuntu2204:~# killall bigworker; echo case_sensitive_rc=$?
bigworker: no process found
case_sensitive_rc=1
root@ubuntu2204:~# killall -I bigworker; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.5; pgrep -x BigWorker || echo bigworker-killed
bigworker-killed
默认精确匹配对大小写一刀切(rc=1 无进程),-I 用不区分大小写的方式命中并杀死 BigWorker(rc=0,复查无残留)。
3.12 -y/-o:按进程启动时间过滤
-y TIME 只杀"比 TIME 更年轻"(新启动)的进程,-o TIME 只杀"比 TIME 更老"的进程。TIME 是浮点数+单位后缀:s 秒(默认)、m 分、h 时、d 天、w 周、M 月、y 年。先起旧进程等 3 秒再起新进程:
root@ubuntu2204:~# /root/demo/killall/myworker 300 & OLD=$!; sleep 3; /root/demo/killall/myworker 300 & NEW=$!; sleep 0.3; echo old=$OLD new=$NEW
old=96 new=98
root@ubuntu2204:~# killall -o 2s myworker; echo rc=$?; sleep 0.3; kill -0 $OLD 2>/dev/null || echo old-killed; kill -0 $NEW 2>/dev/null && echo new-still-alive
rc=0
old-killed
new-still-alive
-o 2s 只命中启动超过 2 秒的旧进程(96 死、98 存活)。再用 -y 5s 收掉新进程:
root@ubuntu2204:~# killall -y 5s myworker; echo rc=$?; sleep 0.3; kill -0 $NEW 2>/dev/null || echo new-killed
rc=0
new-killed
💡 要点:
-o/-y组合可实现"只杀僵死的老实例、放过刚拉起的新实例",是滚动重启脚本的常用过滤器。
3.13 -g:杀整个进程组
-g 把信号从"单个进程"升级为"目标所在进程组"(向 -pgid 发送),且每组只发一次。演示组:改名 bash pg2 运行脚本拉起 2 个子 sleep(三成员同组 230):
root@ubuntu2204:~# setsid /root/demo/killall/pg2 /root/demo/killall/pg2-demo.sh >/dev/null 2>&1 & sleep 0.5; PGID=$(ps -o pgid= -p $(pgrep -x pg2) | tr -d ' '); echo pgid=$PGID; ps -o pid,ppid,pgid,comm -g $PGID
pgid=230
PID PPID PGID COMMAND
230 222 230 pg2
232 230 230 sleep
233 230 230 sleep
root@ubuntu2204:~# killall -v -g pg2; echo rc=$?
Killed pg2(pgid 230) with signal 15
rc=0
root@ubuntu2204:~# sleep 0.5; ps -o pid,ppid,pgid,comm -g $PGID 2>/dev/null; pgrep -g $PGID || echo group-empty
PID PPID PGID COMMAND
232 1 230 sleep <defunct>
233 1 230 sleep <defunct>
232
233
verbose 行 Killed pg2(pgid 230) with signal 15 证实信号发给了整个组 230:组长 pg2(230)立即消失,两个子 sleep(232/233)也一并终止——它们的父进程先死,被 1 号进程收养后尚未收尸,所以 ps 里显示 <defunct>(僵尸态),pgrep 仍能列出条目(详见 5 章 Q5)。作为对照,手动组信号 kill -TERM -$PGID 的效果与之一致:
root@ubuntu2204:~# kill -TERM -$PGID; echo rc=$?; sleep 0.5; ps -o pid,pgid,comm -g $PGID 2>/dev/null; pgrep -g $PGID || echo group-empty-by-kill
rc=0
PID PGID COMMAND
245 243 sleep <defunct>
246 243 sleep <defunct>
245
246
💡 要点:
killall -g NAME≈kill -TERM -PGID的"按名字版"。适合树状脚本全家桶(shell + 它拉起的子进程),比逐个杀子进程干净。
3.14 -n:匹配同 namespace 的进程
-n PID 把匹配范围限定为"与参考 PID 同一 PID namespace 的进程",用于多租户/嵌套 namespace 环境防越界。参考 1 号进程(本环境所有进程与其同 namespace)杀 myworker:
root@ubuntu2204:~# killall -q myworker 2>/dev/null; /root/demo/killall/myworker 300 & sleep 0.3; pgrep -x myworker
162
root@ubuntu2204:~# killall -n 1 myworker; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -x myworker || echo ns-match-killed
ns-match-killed
-n 1 命中并杀死了同 namespace 内的 162 号进程。在嵌套 namespace 场景里,若目标进程在别的 namespace,同名也不会被波及。
3.15 -Z:按 security context 匹配
-Z REGEXP 只杀 security context 匹配正则的进程(SELinux 环境),且按帮助文本要求必须放在其他参数之前。本环境无 SELinux label,用兜底正则 .* 匹配全部 context:
root@ubuntu2204:~# killall -q myworker 2>/dev/null; /root/demo/killall/myworker 300 & sleep 0.3; killall -Z '.*' myworker; echo rc=$?
rc=0
rc=0 说明 context 匹配通路工作正常(.* 匹配到目标的空/默认 context)。在启用 SELinux 的系统上,-Z 可实现"只杀某个域(domain)的同名进程"这类细粒度控制。
3.16 长进程名(>15 字符):截断匹配与 -e 的实测真相
内核给 comm 的缓存只有 16 字节(名字最长 15 字符),长名进程的 comm 会被截断。创建长名进程后看 ps 的 comm 列:
root@ubuntu2204:~# /root/demo/killall/this-is-a-long-cmdname-x 300 & LP=$!; sleep 0.3; ps -o pid,comm,args -p $LP
PID COMMAND COMMAND
489 this-is-a-long- /root/demo/killall/this-is-a-long-cmdname-x 300
comm 已截断为 this-is-a-long-(15 字符)。killall 对长名的实测行为分两层:
第一层:默认就能杀长名进程(按截断后的前 15 字符匹配):
root@ubuntu2204:~# killall this-is-a-long-cmdname-x; echo default_rc=$?
default_rc=0
root@ubuntu2204:~# ps -o pid,comm -p $LP 2>/dev/null || echo default-killed-longname
PID COMMAND
default-killed-longname
第二层:同前缀不同全名会被"误伤",加 -e 也救不了。 起 this-is-a-long-cmdname-X 与 -Y 两个进程(comm 同为 this-is-a-long-),只点名 Y:
root@ubuntu2204:~# '/root/demo/killall/this-is-a-long-cmdname-X' 300 & '/root/demo/killall/this-is-a-long-cmdname-Y' 300 & sleep 0.3; ps -eo pid,comm,args | grep '[t]his-is'
193 this-is-a-long- /root/demo/killall/this-is-a-long-cmdname-X 300
194 this-is-a-long- /root/demo/killall/this-is-a-long-cmdname-Y 300
root@ubuntu2204:~# killall this-is-a-long-cmdname-Y; echo default_rc=$?
default_rc=0
root@ubuntu2204:~# sleep 0.3; ps -eo pid,comm | grep '[t]his-is' || echo both-killed-by-default-match
both-killed-by-default-match
-Y 把 X 也带走了(默认按前 15 字符匹配,两者 comm 相同)。那 -e,--exact("require exact match for very long names")呢?加 -v -e 再来一轮,verbose 会如实打印它杀的是谁:
root@ubuntu2204:~# '/root/demo/killall/this-is-a-long-cmdname-X' 300 & '/root/demo/killall/this-is-a-long-cmdname-Y' 300 & sleep 0.3; ps -eo pid,comm | grep '[t]his-is'
271 this-is-a-long-
272 this-is-a-long-
root@ubuntu2204:~# killall -v -e this-is-a-long-cmdname-Y; echo rc=$?
Killed this-is-a-long-(271) with signal 15
Killed this-is-a-long-(272) with signal 15
rc=0
root@ubuntu2204:~# sleep 0.3; ps -eo pid,comm | grep '[t]his-is' || echo all-dead
all-dead
verbose 显示 271、272 两个都被杀——-e 并未按完整名区分 X 与 Y。原因(结合 psmisc 23.4 源码与实测交叉验证):长名精确匹配分支要求 comm 达到 killall 内部缓冲上限(31 字符)才去读 cmdline 做全名比对,而 Ubuntu 22.04 内核的 comm 上限是 15 字符,该分支永远不会触发,匹配实际始终落在"comm 前 15 字符"上。-e 的另一价值在"进程 argv[0] 与 comm 不一致"的场景:用 exec -a 给长名 sleep 换 argv[0] 后,默认匹配照样按 comm 杀:
root@ubuntu2204:~# bash -c 'exec -a totally-different-name /root/demo/killall/this-is-a-long-cmdname-S 300' & sleep 0.3; ps -eo pid,comm,args | grep '[t]his-is'
256 this-is-a-long- totally-different-name 300
root@ubuntu2204:~# killall -v this-is-a-long-cmdname-S; echo default_rc=$?; sleep 0.3; pgrep -f totally-different || echo killed-by-default-comm-match
Killed this-is-a-long-(256) with signal 15
default_rc=0
killed-by-default-comm-match
verbose 打印的是 comm(this-is-a-long-)而非 argv[0],坐实"按 comm 截断名匹配"。
💡 要点:在 Ubuntu 22.04(内核 comm 15 字符上限)上,killall 对 >15 字符进程名只能按前 15 字符匹配,
-e无法把匹配精度提升到全名。需要按全名区分同前缀长名进程时,改用pkill -f/pgrep -f(匹配完整命令行)先定位 PID 再 kill(对照实验见 5 章 Q2)。
四、实战示例
4.1 正则批量杀并逐行回显:-v -r
场景:按名字前缀清理一批进程,且要留下"击杀清单"便于审计。-r 提供正则、-v 提供回显:
root@ubuntu2204:~# /root/demo/killall/myworker 300 & /root/demo/killall/myworker 300 & sleep 0.3; killall -v -r 'mywor.*'; echo rc=$?
Killed myworker(115) with signal 15
Killed myworker(116) with signal 15
rc=0
两个命中进程各占一行、PID 清晰可查。审计要求高时,把这行输出重定向进部署日志即可。
4.2 重启模式:-w -q 停旧起新
场景:脚本里"先确保旧实例全死透,再拉起新实例"。-w 等死、-q 压掉无进程时的报错,两参数组合让命令在"有/无旧实例"两种状态下都安静可用:
root@ubuntu2204:~# killall -q -w myworker 2>/dev/null || echo no-worker-running
no-worker-running
root@ubuntu2204:~# /root/demo/killall/myworker 300 & sleep 0.3
root@ubuntu2204:~# killall -w -q myworker && echo stopped-ok; /root/demo/killall/myworker 300 & sleep 0.3; echo new_worker_pid=$(pgrep -x myworker | head -1)
stopped-ok
new_worker_pid=122
第一轮无旧实例:-q -w 静默返回非零,脚本走 || 分支;第二轮有旧实例:-w -q 等其死透(stopped-ok)后立即起新实例(新 PID 122)。killall -w -q NAME && start 是最简重启习语。
4.3 跨用户同名进程全清与误伤防护
场景:清理所有用户同名的工作进程。不带 -u 时 killall 命中所有用户的同名进程(root 与 tester 各一个 myworker):
root@ubuntu2204:~# /root/demo/killall/myworker 300 & su tester -c '/tmp/myworker 300' & sleep 0.5; ps -eo pid,user,comm | grep '[m]yworker'
86 root myworker
89 tester myworker
root@ubuntu2204:~# killall myworker; echo rc=$?
rc=0
Terminated
root@ubuntu2204:~# sleep 0.5; ps -eo pid,user,comm | grep '[m]yworker' || echo both-users-myworker-dead
both-users-myworker-dead
两个用户的 myworker 同时清空。反过来若只想杀 tester 的,必须带 -u tester(见 3.9,root 的进程安然无恙)。防护口诀:全清裸奔,点杀加 -u。
4.4 默认 TERM 可被捕获:优雅退出
场景:验证 killall 默认信号的业务友好性。工作进程用 bash trap 捕获 TERM 做清理后退出(trap 脚本要点:trap 'echo "caught TERM from killall, cleanup then exit"; exit 0' TERM):
root@ubuntu2204:~# cp -f /bin/bash /root/demo/killall/trapworker
root@ubuntu2204:~# /root/demo/killall/trapworker /root/demo/killall/trap-demo.sh & TW=$!; sleep 0.5; killall trapworker; wait $TW 2>/dev/null; echo worker_rc=$?
trap-worker pid=504
caught TERM from killall, cleanup then exit
worker_rc=0
killall trapworker 发出的 TERM 被工作进程捕获,打印清理消息后以 0 退出(worker_rc=0)。这正是"默认 TERM 优先于 KILL"的价值:给应用留出落盘、释放锁、摘流量等收尾机会。
4.5 清理脚本的收尾习语:-q 双保险
场景:实验/发布脚本结尾的"扫尾段",要求无论进程在不在都不报错、不污染输出。多种名字逐个静默清理并用 pkill -f 兜底命令行残留:
root@ubuntu2204:~# killall -q myworker 2>/dev/null; killall -q BigWorker 2>/dev/null; killall -q this-is-a-long-cmdname-x 2>/dev/null; pkill -f 'sleep (300|77|88)' 2>/dev/null; sleep 0.3; echo cleanup-done
cleanup-done
每个 killall -q NAME 2>/dev/null 独立执行、失败不中断;sleep 0.3 给内核回收留出时间;最后一条 echo 作为"扫尾完成"的显式标记。这套习语可直接复用到任何自动化脚本结尾。
五、注意事项与常见问题
Q1:killall、kill、pkill 三者怎么选?
三者的分工在 2.1 技术拓展表已给全貌,核心实测差异是匹配精度:对部分名 wor,killall wor 报 wor: no process found 且 rc=1、目标幸存,而 pkill wor 直接命中并杀死(见 3.2 对照实验)。选型建议——已知 PID 用 kill;按精确名批量终止用 killall(误伤面最小);需要模式/全命令行匹配用 pkill(先 pgrep 预览再落刀)。
Q2:进程名超过 15 字符时,-e 能防止同前缀误杀吗?
不能(本环境实测)。内核 comm 上限 15 字符,killall 的匹配始终落在前 15 字符:点名 this-is-a-long-cmdname-Y 会把同前缀的 -X 一起杀掉,加 -e 后 verbose 依旧显示两个进程都被杀(见 3.16 两轮实验)。需要全名精度时用 pkill -f/pgrep -f 按完整命令行定位 PID,再对 PID 精确 kill——不要依赖 -e。
Q3:直接 killall NAME 有哪些风险?
三个:① 跨用户误伤——同名进程属于所有用户,root 执行会连 tester 的进程一起杀(4.3 实测),多人共用的机器务必带 -u;② 同前缀长名误伤(Q2);③ 关键系统进程同名风险——先 pgrep -x NAME/ps -eo pid,comm | grep NAME 确认目标清单,或加 -i 逐个确认(3.3),再执行 killall。
Q4:脚本里如何优雅处理"进程可能不存在"?
用退出码而非报错文本:killall 无匹配返回 1、成功返回 0,-q 压掉 stderr 报错文本(3.7 实测两种情形退出码一致)。推荐 killall -q NAME 2>/dev/null || true(清理语义)或 killall -w -q NAME && echo stopped-ok(重启语义,见 4.2)。
Q5:进程被 killall 杀掉后,ps/pgrep 为什么还能看到它(<defunct>)?
<defunct> 是僵尸态:进程已终止,但其父进程先死或未调用 wait 收尸,进程表条目被 1 号进程收养后暂未释放。-g 实验里两个子 sleep 显示 <defunct>、pgrep 仍列出 PID(3.13),即"已死未收尸",并非"没杀掉"。判断生死用 kill -0 PID(活着返回 0)或看 ps 的 STAT 列是否 Z。
Q6:为什么选项打错一个,整条命令一个进程都没杀?
killall 在参数解析阶段就失败:实测混入不存在的 -x 后立即打印完整 Usage 并返回 1(3.9 的 ⚠️ 实例)。幸运的是报错回显自带全部合法选项表,照着修即可。也提醒写脚本时把 killall 的退出码纳入判断,别让"参数写错"被误当成"进程不存在"。
六、参考链接
- killall(1) 手册页(Ubuntu 22.04 jammy):https://manpages.ubuntu.com/manpages/jammy/en/man1/killall.1.html
- psmisc 软件包页(Ubuntu):https://packages.ubuntu.com/jammy/psmisc
- psmisc 官方源码仓库(GitLab):https://gitlab.com/psmisc/psmisc
- signal(7) 信号清单(man7.org):https://man7.org/linux/man-pages/man7/signal.7.html
- 同系列对照篇:《pkill 命令详解与参数实测(Ubuntu 22.04)》《kill 命令详解与参数实测(Ubuntu 22.04)》