为什么 sudo 不总是生成 shell 子进程?

为什么 sudo 不总是生成 shell 子进程?

我试图理解为什么某些 shell 在调用时似乎会受到特殊处理须藤。例如,似乎有两种可能的行为:

“隐含”组(是的直接子代须藤,中间没有外壳):

$ sudo pstree -s $$
systemd───login───bash───sudo───pstree
$ sudo bash -c 'pstree -s $$'
systemd───login───bash───sudo───pstree
$ sudo zsh -c 'pstree -s $$'
systemd───login───bash───sudo───pstree
$ sudo dash -c 'pstree -s $$'
systemd───login───bash───sudo───pstree

“显式”组(shell 是须藤):

$ sudo ksh -c 'pstree -s $$'
systemd───login───bash───sudo───ksh───pstree
$ sudo tcsh -c 'pstree -s $$'
systemd───login───bash───sudo───tcsh───pstree
$ sudo fish -c 'pstree -s $fish_pid'
systemd───login───bash───sudo───fish───pstree

显然两者之间似乎发生了某种整合须藤和一些 shell,但我找不到任何相关文档。我还 grep 了两者的源代码须藤巴什但也找不到任何线索。

这另一个问题似乎相关:为什么 (...) 在后台运行时不生成新的子进程?

我的版本须藤巴什是:

$ sudo --version
Sudo version 1.8.29
...
$ bash --version
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
...

答案1

不,这不是 shell 和 sudo 之间的交互;这是负责执行任务的 shell 将其自身替换为您运行的命令!

您可以从整个事物中删除 sudo 以获得相同的结果;例如,我在 alacritty 终端中运行 zsh:

$> bash -c 'pstree -s $$'
systemd───alacritty───zsh───pstree

这里没有bash!

我们可以通过运行来验证会发生什么

$> strace -o /tmp/bash-pstree.strace bash -c 'pstree -s $$'
systemd───alacritty───zsh───pstree
$> bat /tmp/bash-pstree.strace # or just less /tmp/.... ; bat is just a nice code highlighter

在那里我们看到

$> grep execve /tmp/bash-pstree.strace
execve("/usr/bin/bash", ["bash", "-c", "pstree -s $$"], 0x7ffc02c52d30 /* 102 vars */) = 0
execve("/usr/bin/pstree", ["pstree", "-s", "35735"], 0x55b90ec78d00 /* 102 vars */) = 

因此,第一个 execve 是 bash 被调用,第二个是 bash 用 pstree 替换自身 - 之后实际上就不存在 bash 了!其间不会发生分叉/克隆。

当然,这只适用于最后的命令链中的命令。如果我们将自己替换为之前运行的命令,则之后我们将无法执行任何操作。我们实际上可以很容易地验证这一点:

$> bash -c 'pstree -p -s $$; pstree -p -s $$'                                                                                                                                                                                                        
systemd(1)───alacritty(34604)───zsh(34609)───bash(39257)───pstree(39258)
systemd(1)───alacritty(34604)───zsh(34609)───pstree(39257)

看,这里第一个 pstree 实际上是在 bash 创建的进程中运行的,第二个 pstree 由同一进程运行,替换了 bash。

用它运行的程序替换 shell 当然是很好的,从资源角度来看:我们尽早生成所有文件句柄、内存、锁等。

我不知道为什么有些 shell 会这样做,而其他 shell 则不会(它们可能在执行指定的命令之前使用forkclone复制自己的进程)。execve可能,这是开发人员从未想到的优化(如果您有一个像 一样丰富的 shell fish,为什么要早一点保护几 kB RAM?没有人会尝试从 shell 脚本生成 10000 条鱼来运行命令!) ,或者引入特殊情况在软件架构上很尴尬,或者是不可能的,因为 shell 保持对启动进程的一些控制,因此仍然需要存在才能例如接收信号或执行 IPC。当然,有些 shell 非常古老,因此可能有点简单(最后一次ksh发布是 10 年前!)。

相关内容