启用Job Control时子shell后台进程组行为疑问咨询
先看你编写的测试脚本:
#!/bin/bash set -e set -m ( echo "waiting to be killed..." sleep inf ) & bg_pid=$! echo $bg_pid sleep 5 echo "kill $bg_pid" kill -- "-$bg_pid"
set -m用于启用Job Control,按预期逻辑,启用后子shell会进入独立进程组——如果不启用该选项,执行kill -- "-$bg_pid"会提示No such process,因为子shell与父shell同属一个进程组。
但实际运行脚本时,在执行kill前打印的进程层级如下:
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 173828 173890 173890 173890 pts/6 206742 Ss 1000 0:00 -bash 173890 206740 206740 173890 pts/6 206742 S 1000 0:00 \_ /bin/bash ./test1.sh 206740 206741 206741 173890 pts/6 206742 S 1000 0:00 \_ /bin/bash ./test1.s 206741 206743 206741 173890 pts/6 206742 S 1000 0:00 | \_ sleep inf 206740 206742 206742 173890 pts/6 206742 S+ 1000 0:00 \_ sleep 5
可以看到sleep inf(PID 206743)和它的父进程(子shell PID 206741)同属进程组206741,背后的原因可以从以下几点分析:
核心原因拆解
子shell已获得独立进程组,但内部前台命令不会新建进程组
启用set -m后,父shell确实为后台运行的子shell(PID 206741)分配了独立进程组(PGID=206741),这部分符合预期。而子shell里执行的sleep inf是它的前台作业——bash的默认逻辑是:对于子shell内的单个前台命令,不会为其单独创建新进程组,除非子shell内将命令放到后台运行。子shell继承Job Control状态,但无需拆分进程组
子shell会继承父shell的set -m设置,但只有当子shell内部存在后台作业(比如写成sleep inf &)时,才会触发新进程组的创建。当前场景下子shell仅运行一个前台sleep命令,自然和子shell同属一个进程组。kill -- "-$bg_pid"的实际作用
这里的-$bg_pid是向进程组206741发送信号,因此即使sleep inf和子shell同属一个进程组,该kill命令依然能同时终止子shell和sleep inf进程,因为二者都在目标进程组内。
内容的提问来源于stack exchange,提问作者Erich Shan

