我从安装了 OpenSSH 服务器的 Ubuntu 构建了一个 docker 镜像。假设我通过 ssh 请求一个简单的命令
ssh [email protected] "sleep 10"
然后,ps aux --forest
容器内部给出了以下内容:
SER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 18 0.3 0.0 18508 3500 pts/0 Ss 16:12 0:00 bash
root 37 0.0 0.0 34404 2884 pts/0 R+ 16:12 0:00 \_ ps aux --forest
root 16 0.0 0.0 72300 3280 ? Ss 16:12 0:00 /usr/sbin/sshd
root 34 0.0 0.0 74656 6648 ? Ss 16:12 0:00 \_ sshd: root@notty
root 36 0.0 0.0 4532 744 ? Ss 16:12 0:00 \_ sleep 1000
但是当我执行更复杂的命令时
ssh [email protected] "sleep 1000; sleep 1"
它现在生成一个 bash shell 并将我的命令传递给它:
root 18 0.1 0.0 18508 3500 pts/0 Ss 16:12 0:00 bash
root 43 0.0 0.0 34404 2896 pts/0 R+ 16:13 0:00 \_ ps aux --forest
root 16 0.0 0.0 72300 3280 ? Ss 16:12 0:00 /usr/sbin/sshd
root 39 0.0 0.0 74656 6712 ? Ss 16:13 0:00 \_ sshd: root@notty
root 41 0.0 0.0 9920 1312 ? Ss 16:13 0:00 \_ bash -c sleep 1000; sleep 1
root 42 0.0 0.0 4532 776 ? S 16:13 0:00 \_ sleep 1000
那么,哪个部分决定是否调用 shell?这由 SSHd 控制吗?如果是这样,有没有办法强制 SSHd 始终调用 shell?
PS 我知道在 Ruby 中,Kernel.exec
该函数是根据元字符(如;
和)选择生成或不生成 shell 的&
,所以也许在我的情况下,选择不是在应用程序级别做出的?
答案1
据我所知,sshd
它不执行此类优化。如果它执行了,那就没什么意义了,因为sshd
它应该使用特定于用户的命令解释器,通常它可以是任何东西(不一定是真正的 shell)。即使解释器是/bin/bash
,它也可能是经过修改的bash
,在启动时会做一些不寻常的事情。因为sshd
无法知道这一点,所以它真的不应该干扰。
无论分配给用户什么命令解释器,它总是会生成。在你的情况下是bash
。
您观察到的是 Bash 本身的优化。在某些情况下,如果 Bash 能够判断在执行最后一个命令期间和之后绝对没有什么可做的,那么它将执行exec
最后一个命令而不是正常运行它。这意味着该命令将替换bash
而不创建新进程。
在您的例子中bash
,是 的子级sshd
。当它用某个命令替换自身时,该命令将成为子级。这给人一种印象,即 shell 从未存在过。
请参阅以下示例(使用 Bash 4.4.20 测试):
bash -c ' exec sleep 5 ' & sleep 1; ps -p "$!"
您会看到,
sleep
因为exec
。这是意料之中的。bash -c ' sleep 5 ' & sleep 1; ps -p "$!"
也是
sleep
因为优化。bash -c ' sleep 5 true ' & sleep 1; ps -p "$!"
这次是
bash
。shell 无法隐式执行,exec sleep 5
因为稍后还要true
运行。bash -c ' sleep 5 sleep 4 ' & sleep 1; ps -p "$!"; sleep 5; ps -p "$!"
首先你会看到
bash
;这是第一个sleep
运行时的情况。然后你会看到(第二个)sleep
具有相同的 PID。这有点有趣(特别是与上面的相比):
bash -c ' sleep 5; sleep 4 ' & sleep 1; ps -p "$!"; sleep 5; ps -p "$!"
这次
bash
没有exec
第二个sleep
,尽管代码看起来与前一个相同。我不知道这是否只是缺乏优化,或者有一个不明显的原因exec
。最后:
bash -c ' 陷阱“echo foo”SIGUSR1 睡 5 '&睡眠1;ps -p“$!”
你会看到
bash
。shell 知道它需要保留以防信号到达,因此它不会用 替换自己sleep 5
。