Bash

為什麼這些 bash fork 炸彈的工作方式不同,其中 & 的意義是什麼?

  • June 18, 2021

我知道普通的叉子炸彈是如何工作的,但我真的不明白為什麼需要在普通 bash 叉子炸彈末尾的 & 以及為什麼這些腳本的行為不同:

:(){ (:) | (:) }; :

:(){ : | :& }; :

前者在讓我回到登錄螢幕之前會導致 CPU 使用率飆升。相反,後者只會導致我的系統凍結,迫使我硬重啟。這是為什麼?兩者都在不斷創建新流程,那麼為什麼系統的行為會有所不同呢?

這兩個腳本的行為也不同於

:(){ : | : }; :

這根本不會引起任何問題,即使我希望它們是相似的。bash 手冊頁指出管道中的命令已經在子 shell 中執行,所以我相信:| : 應該已經足夠了。我相信&應該只在一個新的子shell中執行管道,但為什麼會發生如此大的變化?

編輯:使用 htop 並限制程序數量,我可以看到第一個變體創建了一個實際的程序樹,第二個變體在同一級別上創建了所有程序,最後一個變體似乎沒有創建任何程序一點也不。這讓我更加困惑,但也許它以某種方式有所幫助?

警告不要試圖在生產機器上執行它。只是不要。 警告:要嘗試任何“炸彈”,請確保ulimit -u正在使用中。參見下文$$ a $$.

讓我們定義一個函式來獲取 PID 和日期(時間):

bize:~$ d(){ printf '%7s %07d %s\n' "$1" "$BASHPID" "$(date +'%H:%M:%S')"; }

一個簡單的、非問題bomb的新使用者功能(保護自己:閱讀$$ a $$):

bize:~$ bomb() { d START; echo "yes"; sleep 1; d END; } >&2

當呼叫該函式以執行時,如下所示:

bize:~$ bomb
 START 0002786 23:07:34
yes
   END 0002786 23:07:35
bize:~$

執行命令date,然後列印“是”,休眠 1 秒,然後關閉命令date,最後,函式退出列印新的命令提示符。沒有什麼花哨。

| 管道

當我們這樣呼叫函式時:

bize:~$ bomb | bomb
 START 0003365 23:11:34
yes
 START 0003366 23:11:34
yes
   END 0003365 23:11:35
   END 0003366 23:11:35
bize:~$

兩個命令同時啟動,兩個命令都將在 1 秒後結束,然後提示返回。

這就是 pipe|並行啟動兩個程序的原因。

& 背景

如果我們更改通話添加結尾&

bize:~$ bomb | bomb &
[1] 3380
bize:~$
 START 0003379 23:14:14
yes
 START 0003380 23:14:14
yes
   END 0003379 23:14:15
   END 0003380 23:14:15

提示立即返回(所有操作都發送到後台),並且兩個命令像以前一樣執行。[1]請注意在程序的 PID 之前列印的“作業號”的值3380。稍後,將列印相同的數字以指示管道已結束:

[1]+  Done                    bomb | bomb

這就是 的效果&

這就是&: 讓程序更快啟動的原因。

更簡單的名字

我們可以創建一個簡單呼叫的函式b來執行這兩個命令。輸入三行:

bize:~$ b(){
> bomb | bomb
> }

並執行為:

bize:~$ b
 START 0003563 23:21:10
yes
 START 0003564 23:21:10
yes
   END 0003564 23:21:11
   END 0003563 23:21:11

請注意,我們;在定義中使用了 no b(換行符用於分隔元素)。但是,對於一行上的定義,通常使用;,如下所示:

bize:~$ b(){ bomb | bomb ; }

大多數空格也不是強制性的,我們可以寫成等價的(但不太清楚):

bize:~$ b(){ bomb|bomb;}

我們也可以使用a&來分隔}(並將兩個程序發送到後台)。

炸彈。

如果我們讓函式咬住它的尾巴(通過呼叫它自己),我們就會得到“叉子炸彈”:

bize:~$ b(){ b|b;}       ### May look better as b(){ b | b ; } but does the same.

為了讓它更快地呼叫更多函式,請將管道發送到後台。

bize:~$ b(){ b|b&}       ### Usually written as b(){ b|b& }

如果我們在 required 之後附加對函式的第一次呼叫並將;名稱更改為:我們得到:

bize:~$ :(){ :|:&};:

通常寫為:(){ :|:& }; :

或者,以一種有趣的方式寫成,用其他名字(雪人):

☃(){ ☃|☃&};☃

ulimit(您應該在執行此之前設置)將使提示在出現大量錯誤後很快返回(當錯誤列表停止以獲取提示時按輸入鍵)。

這被稱為“分叉炸彈”的原因是外殼啟動子外殼的方式是分叉正在執行的外殼,然後使用執行命令呼叫 exec() 到分叉的程序。

管道將“分叉”兩個新程序。做到無窮遠會導致炸彈。

或者是最初被稱為兔子的兔子,因為它的繁殖速度非常快。


定時:

  1. :(){ (:) | (:) }; time :

終止

實數 0m45.627s 2. :(){ : | :; }; time :

終止

實0m15.283s 3. :(){ : | :& }; time :

real 0m00.002 s

仍在執行


你的例子:

  1. :(){ (:) | (:) }; :

第二次關閉)分隔 的地方}是更複雜的:(){ :|:;};:. 無論如何,管道中的每個命令都會在子外殼中呼叫。哪個是效果()。 2. :(){ : | :& }; :

是更快的版本,寫成沒有空格::(){(:)|:&};:(13 個字元)。 3. :(){ : | : }; : ### 在 zsh 中有效,但在 bash 中無效。

有語法錯誤(在 bash 中),在結束之前需要一個元字元}

如下所示:

:(){ : | :; }; :

$$ a $$ 創建一個新的干淨使用者(我會打電話給我的bize)。在控制台中登錄這個新使用者sudo -i -u bize,或者:

$ su - bize
Password: 
bize:~$

檢查然後更改max user processes限制:

bize:~$ ulimit -a           ### List all limits (I show only `-u`)
max user processes              (-u) 63931
bize:~$ ulimit -u 10        ### Low
bize:~$ ulimit -a
max user processes              (-u) 1000

僅使用 10 個作品,因為只有一個新使用者:bize. 它更容易呼叫killall -u bize並讓系統擺脫大多數(不是全部)炸彈。請不要問哪些仍然有效,我不會說。但仍然:相當低,但為了安全起見,適應你的系統

將確保“分叉炸彈”不會使您的系統崩潰

進一步閱讀:

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