我试图理解为什么某些 shell 在使用sudo调用时似乎得到了特殊处理。例如,似乎有两种可能的行为:
“隐式”组(pstree是sudo的直接子代,中间没有外壳):
$ 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的直接子代):
$ 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
显然sudo和一些 shell之间似乎发生了某种集成,但我找不到关于它的文档。我还 grepped 了sudo和bash的源代码,但也找不到任何线索。
另一个问题似乎是相关的:为什么 (...) 在后台运行时不会产生新的子进程?
我的sudo和bash版本是:
$ sudo --version
Sudo version 1.8.29
...
$ bash --version
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
...
不,这不是你的 shell 和 sudo 之间的交互;就是负责执行的 shell 将自己替换为您运行的命令!
您可以从整个事情中删除 sudo 以获得相同的结果;例如,我在 alacritty 终端中运行 zsh:
这里没有bash!
我们可以通过运行来验证发生了什么
我们在那里看到
所以,第一个 execve 是 bash 被调用,第二个是 bash 用 pstree 替换自己 - 之后实际上没有 bash 存在!两者之间不会发生分叉/克隆。
当然,这只适用于命令链中的最后一个命令。如果我们将自己替换为之前要运行的命令,那么之后我们将无法执行任何操作。我们实际上可以很容易地验证这一点:
看,这里第一个 pstree 实际上是在由 bash 创建的进程中运行的,第二个由同一进程运行,替换 bash。
用它运行的程序替换 shell 当然是好的,在资源方面:我们在早期就产生了所有的文件句柄内存、锁等。
我不知道为什么有些 shell 会这样做,而其他 shell 则不会(他们可能在指定命令之前使用
fork
或复制自己的进程)。可能,这是开发人员从未想到过的优化(如果你有一个像,或者引入特殊情况在软件架构上很尴尬,或者这是不可能的,因为外壳对启动的进程保持一些控制,因此仍然需要存在才能接收信号或进行 IPC。当然,有些 shell 非常陈旧,因此可能有点简单(上一个版本是 10 年前!)。clone
execve
fish
ksh