mes*_*esr 7 bash process zsh sudo dash
我试图理解为什么某些 shell 在使用sudo调用时似乎会受到特殊处理调用时似乎会受到特殊处理。例如,似乎有两种可能的行为:
\n“隐式”组(pstree是sudo的直接子级,中间没有 shell):
\n$ sudo pstree -s $$\nsystemd\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80login\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80sudo\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree\n$ sudo bash -c \'pstree -s $$\'\nsystemd\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80login\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80sudo\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree\n$ sudo zsh -c \'pstree -s $$\'\nsystemd\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80login\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80sudo\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree\n$ sudo dash -c \'pstree -s $$\'\nsystemd\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80login\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80sudo\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree\nRun Code Online (Sandbox Code Playgroud)\n“显式”组(shell 是sudo的直接子级)的直接子级):
\n$ sudo ksh -c \'pstree -s $$\'\nsystemd\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80login\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80sudo\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80ksh\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree\n$ sudo tcsh -c \'pstree -s $$\'\nsystemd\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80login\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80sudo\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80tcsh\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree\n$ sudo fish -c \'pstree -s $fish_pid\'\nsystemd\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80login\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80sudo\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80fish\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree\nRun Code Online (Sandbox Code Playgroud)\n显然sudo和一些 shell 之间似乎发生了某种集成,但我找不到任何相关文档。我还 grep 了sudo和bash的源代码,但也找不到任何线索。
这个另一个问题似乎相关:Why (...) does not spawn a new child process while run in back?
\n我的sudo和bash版本是:
\n$ sudo --version\nSudo version 1.8.29\n...\n$ bash --version\nGNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)\n...\nRun Code Online (Sandbox Code Playgroud)\n
Mar*_*ler 21
不,这不是 shell 和 sudo 之间的交互;这是负责执行任务的 shell 将其自身替换为您运行的命令!
\n您可以从整个事物中删除 sudo 以获得相同的结果;例如,我在 alacritty 终端中运行 zsh:
\n$> bash -c \'pstree -s $$\'\nsystemd\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80alacritty\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80zsh\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree\nRun Code Online (Sandbox Code Playgroud)\n这里没有bash!
\n我们可以通过运行来验证会发生什么
\n$> strace -o /tmp/bash-pstree.strace bash -c \'pstree -s $$\'\nsystemd\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80alacritty\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80zsh\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree\n$> bat /tmp/bash-pstree.strace # or just less /tmp/.... ; bat is just a nice code highlighter\nRun Code Online (Sandbox Code Playgroud)\n在那里我们看到
\n$> grep execve /tmp/bash-pstree.strace\nexecve("/usr/bin/bash", ["bash", "-c", "pstree -s $$"], 0x7ffc02c52d30 /* 102 vars */) = 0\nexecve("/usr/bin/pstree", ["pstree", "-s", "35735"], 0x55b90ec78d00 /* 102 vars */) = \nRun Code Online (Sandbox Code Playgroud)\n所以,第一个 execve 是 bash 被调用,第二个是 bash 用 pstree \xe2\x80\x93 替换自身,之后实际上就没有 bash 存在了!其间不会发生分叉/克隆。
\n当然,这只适用于命令链中的最后一个命令。如果我们将自己替换为之前运行的命令,则之后我们将无法执行任何操作。我们实际上可以很容易地验证这一点:
\n$> bash -c \'pstree -p -s $$; pstree -p -s $$\' \nsystemd(1)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80alacritty(34604)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80zsh(34609)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash(39257)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree(39258)\nsystemd(1)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80alacritty(34604)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80zsh(34609)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree(39257)\nRun Code Online (Sandbox Code Playgroud)\n看,这里第一个 pstree 实际上是在 bash 创建的进程中运行的,第二个 pstree 由同一进程运行,替换了 bash。
\n用它运行的程序替换 shell 当然是很好的,从资源角度来看:我们尽早生成所有文件句柄、内存、锁等。
\n我不知道为什么有些 shell 会这样做,而其他 shell 则不会(它们可能在执行指定的命令之前使用fork或clone复制自己的进程)。execve可能,这是开发人员从未想到的优化(如果您有一个像 一样丰富的 shell fish,为什么要早一点保护几 kB RAM?没有人会尝试从 shell 脚本生成 10000 条鱼来运行命令!),或者引入特殊情况在软件架构上很尴尬,或者是不可能的,因为 shell 保持对启动进程的一些控制,因此仍然需要存在才能例如接收信号或执行 IPC。当然,有些 shell 非常古老,因此可能有点简单(最后一次ksh发布是 10 年前!)。