通常のフォーク爆弾がどのように機能するかがわかりますが、一般的なbashフォーク爆弾の最後に&が必要な理由と、これらのスクリプトの動作が異なる理由がよくわかりません。
:(){ (:) | (:) }; :
そして
:(){ : | :& }; :
前者は、ログイン画面に戻る前にCPU使用率の急上昇を引き起こします。後者の場合、代わりにシステムがフリーズし、強制的に再起動する必要があります。何故ですか?どちらも新しいプロセスを継続的に作成しているのに、システムの動作が異なるのはなぜですか?
どちらのスクリプトも動作が異なります
:(){ : | : }; :
私はそれらが似ていると思っていたとしても、これはまったく問題を引き起こしません。 bashのマニュアルページには、パイプラインのコマンドは既にサブシェルで実行されていると記載されているため、次のように考えています。 :で十分でしょう。私は信じています、新しいサブシェルでパイプラインを実行する必要がありますが、なぜそれがそれほど変わるのですか?
編集:htopを使用してプロセスの量を制限すると、最初のバリアントは実際のプロセスツリーを作成し、2番目のバリアントは同じレベルのすべてのプロセスを作成し、最後のバリアントはプロセスを作成しないように見えましたまったく。これは私をさらに混乱させますが、たぶんそれは何とか助けますか?
警告これを実稼働マシンで実行しようとしないでください。しないでください。警告:「爆弾」を試すには、_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
が実行された後、「yes」が出力され、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:~$
_
ある時点で2つのコマンドが開始され、2つが1秒後に終了し、、次にプロンプトが返されます。
これがパイプ_|
_が2つのプロセスを並行して開始する理由です。
最後の_&
_を追加して呼び出しを変更する場合:
_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
_
プロンプトはすぐに戻り(すべてのアクションがバックグラウンドに送信されます)、2つのコマンドは以前と同様に実行されます。プロセスのPIDの前に出力される「ジョブ番号」の値_[1]
_に注意してください_3380
_。後で、同じ番号が出力され、パイプが終了したことを示します。
_[1]+ Done bomb | bomb
_
それが_&
_の効果です。
これが_&
_:の理由であり、プロセスをより速く開始します。
単にb
という関数を作成して、2つのコマンドを実行できます。 3行で入力:
_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
_
b
の定義では_;
_を使用していないことに注意してください(改行は要素を区切るために使用されました)。ただし、1行で定義する場合は、通常、次のように_;
_を使用します。
_bize:~$ b(){ bomb | bomb ; }
_
ほとんどのスペースも必須ではありません。同等のものを書くことができます(ただし、あまり明確ではありません)。
_bize:~$ b(){ bomb|bomb;}
_
_&
_を使用して_}
_を分離することもできます(2つのプロセスをバックグラウンドに送信します)。
(それ自体を呼び出すことによって)関数を尻尾に食い込ませると、「フォーク爆弾」を取得します。
_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& }
_
必要な_;
_の後に関数の最初の呼び出しを追加し、名前を_:
_に変更すると、次のようになります。
_bize:~$ :(){ :|:&};:
_
通常:(){ :|:& }; :
として記述されます
または、別の名前(雪だるま)を使って楽しい方法で記述します。
_☃(){ ☃|☃&};☃
_
Ulimit(これを実行する前に設定しておく必要があります)を使用すると、多くのエラーの後でプロンプトが非常に迅速に戻ります(プロンプトを取得するためにエラーリストが停止したときにEnterキーを押します)。
これが「フォーク爆弾」と呼ばれる理由は、シェルがサブシェルを開始する方法は、実行中のシェルをフォークし、実行するコマンドを使用してフォークされたプロセスにexec()を呼び出すことです。
パイプは2つの新しいプロセスを「フォーク」します。無限にそれを行うと爆弾が発生します。
またはそれが非常に速く再生するために、最初に呼ばれたウサギ。
:(){ (:) | (:) }; time :
終了
実際の0分45.627秒
:(){ : | :; }; time :
終了
実際の0分15.283秒
:(){ : | :& }; time :
実際の0m00.002秒
まだ実行されています
:(){ (:) | (:) }; :
2番目の終了_)
_が_}
_を区切るところは、:(){ :|:;};:
のより複雑なバージョンです。とにかく、パイプ内の各コマンドはサブシェル内で呼び出されます。これが_()
_の効果です。
:(){ : | :& }; :
スペースがないように記述された高速バージョンです::(){(:)|:&};:
(13文字)。
:(){ : | : }; :
### 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のみを使用しても、1人の新しいユーザーbize
と同じように機能します。 _killall -u bize
_を呼び出して、システムがほとんど(すべてではない)の爆弾を取り除くのを容易にします。どれがまだ機能するか尋ねないでください、私は教えません。それでも: はかなり低いですが、安全面では、システムに適応します 。
これ は、「フォーク爆弾」がシステムを崩壊させないようにします 。
参考文献: