為什麼不能像在 bash 中一樣在 zsh 中殺死 fork 炸彈?
我想讓我的系統對分叉炸彈更有彈性。這可以通過使用 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 啟動一個終端並在其中執行
:(){ :|:& };:
時,什麼都沒有發生。我幾乎立即得到 2048pids.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"
。