为什么 Bash 处理子进程与 Sh 不同

Why is Bash handling child processes different compared to Sh

在Docker中使用的tini init-process提到进程组杀戮默认不激活并给出了以下示例:

docker run krallin/ubuntu-tini sh -c 'sleep 10'

如果我运行这个,然后马上按Ctrl-C,我确实要等10秒直到子进程退出。

但是,如果我使用 bash:

而不是 sh
docker run krallin/ubuntu-tini bash -c 'sleep 10'

然后按Ctrl-C,进程立即退出。

为什么 sh(符号链接到 dash)和 bash 对此子进程的行为不同? 还有Bash怎么杀掉子进程,我还以为Bash默认不传播信号呢?

感谢 chepner 和 Charles Duffy 的回答:

bash -c 有一个隐式优化,如果可能的话,它使用 exec 来替换自己。 sh (dash) 没有这个优化。另见 this observation

验证:

  • 进程树使用 bash:
❯ docker run --name test --rm --detach krallin/ubuntu-tini bash -c 'sleep 60'
03194d48a4dcc8225251fe1e5de2dcbb901c8a9cfd0853ae910bfe4d3735608d
❯ docker exec test ps axfo pid,ppid,args
    PID    PPID COMMAND
      1       0 /usr/bin/tini -- bash -c sleep 60
      7       1 sleep 60
  • 进程树使用 sh:
❯ docker run --name test --rm --detach krallin/ubuntu-tini sh -c 'sleep 60'
e56f207509df4b0b57f8e6b2b2760835f6784a147b200d798dffad112bb11d6a
❯ docker exec test ps axfo pid,ppid,args
    PID    PPID COMMAND
      1       0 /usr/bin/tini -- sh -c sleep 60
      7       1 sh -c sleep 60
      8       7  \_ sleep 60