为什么不能像在 bash 中杀死 fork 炸弹一样在 zsh 中杀死它?

为什么不能像在 bash 中杀死 fork 炸弹一样在 zsh 中杀死它?

我想让我的系统对分叉炸弹更具弹性。这可以通过使用 systemd 的DefaultTasksMax参数或使用所有有效 shellcgrouppids控制器来部分实现,例如通过 cgrulesengd:

*:bash               pids                             users/shells/bash/
*:zsh                pids                             users/shells/zsh/

还有一个用于设置 shell 限制的小脚本:

CGDIR=/sys/fs/cgroup/
mkdir -p       $CGDIR/pids/users/shells/
echo '8192'  > $CGDIR/pids/users/shells/pids.max
mkdir -p       $CGDIR/pids/users/shells/bash/
echo '2048'  > $CGDIR/pids/users/shells/bash/pids.max
mkdir -p       $CGDIR/pids/users/shells/zsh/
echo '1024'  > $CGDIR/pids/users/shells/zsh/pids.max

当我使用 bash shell 启动终端并:(){ :|:& };:在其中执行时,什么也没有发生。我几乎立即得到 2048 pids.current,CPU 利用率为 50-60%。当我关闭终端时,叉子炸弹就会消失,一切都会恢复正常。

当我想使用 zsh shell 对终端执行相同操作时,我立即获得 100% CPU,并且在关闭终端后,fork 炸弹仍然会杀死我的系统。解决此问题的唯一方法是killall -9 zsh在装有其他 shell 的终端中发出几次命令。

为什么不能像在 bash 中杀死 fork 炸弹一样在 zsh 中杀死它?我的意思是,只要关闭终端,炸弹就会被引爆。

答案1

在 中bash,当fork()EAGAIN 失败时,当达到限制时(使用 setrlimit,或使用那些 cgroup pid 限制),bash休眠并在 1 秒后再次尝试,然后在 2 秒后(如果再次失败),然后在 4 秒后,然后它休眠 8 秒并放弃,甚至没有尝试另一个 fork(!))。

然后bash,一旦达到限制(在安静的系统上不到一秒的时间),大多数 bash 进程就会休眠。这就是为什么叉式炸弹bash比其他炮弹的危害要小得多。

关闭终端不会终止这些进程。也许您看到的是 fork 炸弹在 15 秒多一点 (1+2+4+8) 后逐渐消失,此时所有成功启动并进入睡眠状态的进程在其启动后 8 秒同时死亡。第四次尝试分叉。

在 中zsh,没有这样的重试和睡眠,所有进程都 fork 和退出。当一个进程死亡时,就会释放一个可供分叉进程之一使用的进程。

如果你想杀死一个 fork 炸弹,最简单的方法就是杀死整个进程组,使用它killall并不可行,因为killall需要收集进程列表,然后为每个进程单独发送杀死。这对于所有进程都处于睡眠状态的情况来说很好bash,但对于始终生成进程的其他 shell 来说则不然,因此可以在每次终止之间启动进程。

您可以使用 获取 pgid ps -j,并使用 终止进程组kill -- "-$pgid"

相关内容