Debian

為什麼不能像在 bash 中一樣在 zsh 中殺死 fork 炸彈?

  • December 26, 2018

我想讓我的系統對分叉炸彈更有彈性。這可以通過使用 systemd 的DefaultTasksMax參數來部分實現,或者通過對所有有效的 shell 使用cgroup’spids控制器,例如通過 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,其中包含其他外殼。

為什麼不能像在 bash 中一樣在 zsh 中殺死 fork 炸彈?我的意思是,只要關閉終端,炸彈就會被引爆。

bash,當fork()EAGAIN 失敗時達到限制時(使用 setrlimit 或使用那些 cgroup pid 限制),bash休眠並在 1 秒後再次嘗試,然後在 2 秒後再次失敗,然後在 4 秒後,然後它睡眠 8 秒並放棄,甚至沒有嘗試另一個叉子(!))。

然後bash,一旦達到限制(在安靜的系統上的幾分之一秒內),大多數 bash 程序都在休眠。bash這就是為什麼叉形炸彈在其他砲彈中的危害要小得多。

關閉終端不會殺死這些程序。也許你看到的是叉子炸彈在 15 秒多一點(1+2+4+8)後消失,當所有已經成功啟動並進入睡眠狀態的程序在它們的 8 秒後同時死亡時第 4 次嘗試分叉。

zsh中,沒有這樣的重試和休眠,所有程序都分叉退出。當一個人死亡時,這會釋放一個可以由其中一個分叉的程序使用的程序。

如果你想殺死一個fork炸彈,最簡單的方法是殺死整個程序組,使用killall不起作用,因為killall需要收集程序列表,然後為每個程序單獨發送殺死。在所有程序都處於休眠狀態的情況下這很好bash,但對於一直產生程序的其他 shell 則不然,因此可以在每次終止之間啟動程序。

您可以使用 獲取 pgid ps -j,並使用 終止程序組kill -- "-$pgid"

引用自:https://unix.stackexchange.com/questions/485337