回答:
のパイプの結果としてx | y
、フォアグラウンドプロセスグループの一部としてパイプラインを含むサブシェルが作成されます。これにより、サブシェルが(を介してfork()
)無期限に作成され続け、フォーク爆弾が作成されます。
$ for (( i=0; i<3; i++ )); do
> echo "$BASHPID"
> done
16907
16907
16907
$ for (( i=0; i<3; i++ )); do
> echo "$BASHPID" | cat
> done
17195
17197
17199
ただし、フォークは、コードが実行されるまで実際には発生しません。これは、:
コードでの最後の呼び出しです。
フォークボムの仕組みを分解するには:
:()
-という新しい関数を定義する :
{ :|: & }
-バックグラウンドで呼び出し元の関数を呼び出し元の関数の別のインスタンスに再帰的にパイプする関数定義:
-フォークボム機能を呼び出すこれは、メモリを集中的に使用しない傾向がありますが、PIDを消費し、CPUサイクルを消費します。
x | y
、なぜサブシェルが作成されるのですか?私の理解では、bashはaを見るとpipe
、pipe()
システムコールを実行し、2を返しますfds
。これで、command_leftがexec
edになり、出力がcommand_rightに入力として渡されます。これで、command_rightがexec
編集されました。では、なぜBASHPID
毎回違うのですか?
x
そしてy
、2つの別個のプロセスで実行される2つの別個のコマンドなので、2つの別個のサブシェルがあります。x
シェルと同じプロセスで実行する場合、それはx
組み込みである必要があります。
コードの最後のビットは;:
、関数を実行しています:(){ ... }
。これは、分岐が発生している場所です。
セミコロンは最初のコマンドを終了し、別のコマンドを開始しています:
。つまり、functionを呼び出しています。この関数の定義にはそれ自体への呼び出しが含まれて:
おり、この呼び出しの出力はバックグラウンドバージョンにパイプされ:
ます。これはプロセスを無期限に支えます。
関数:()
を呼び出すたびに、C関数を呼び出しますfork()
。最終的に、これはシステム上のすべてのプロセスID(PID)を使い果たします。
|:&
何かを交換して、何が起こっているのかを知ることができます。
1つのターミナルウィンドウでこれを行います。
$ watch "ps -eaf|grep \"[s]leep 61\""
別のウィンドウで、フォークボムのわずかに変更されたバージョンを実行します。このバージョンは、それが何をしているのかを調査できるように、それ自体を抑制しようとします。このバージョンは、関数を呼び出す前に61秒間スリープします:()
。
また、最初の呼び出しも、呼び出された後にバックグラウンドにします。Ctrl+ z、次に入力しbg
ます。
$ :(){ sleep 61; : | : & };:
# control + z
[1]+ Stopped sleep 61
[2] 5845
$ bg
[1]+ sleep 61 &
jobs
最初のウィンドウでコマンドを実行すると、次のように表示されます。
$ jobs
[1]- Running sleep 61 &
[2]+ Running : | : &
数分後:
$ jobs
[1]- Done sleep 61
[2]+ Done : | :
一方、実行している別のウィンドウでwatch
:
Every 2.0s: ps -eaf|grep "[s]leep 61" Sat Aug 31 12:48:14 2013
saml 6112 6108 0 12:47 pts/2 00:00:00 sleep 61
saml 6115 6110 0 12:47 pts/2 00:00:00 sleep 61
saml 6116 6111 0 12:47 pts/2 00:00:00 sleep 61
saml 6117 6109 0 12:47 pts/2 00:00:00 sleep 61
saml 6119 6114 0 12:47 pts/2 00:00:00 sleep 61
saml 6120 6113 0 12:47 pts/2 00:00:00 sleep 61
saml 6122 6118 0 12:47 pts/2 00:00:00 sleep 61
saml 6123 6121 0 12:47 pts/2 00:00:00 sleep 61
そして、ps -auxf
このプロセス階層を示しています:
$ ps -auxf
saml 6245 0.0 0.0 115184 5316 pts/2 S 12:48 0:00 bash
saml 6247 0.0 0.0 100988 468 pts/2 S 12:48 0:00 \_ sleep 61
....
....
saml 6250 0.0 0.0 115184 5328 pts/2 S 12:48 0:00 bash
saml 6268 0.0 0.0 100988 468 pts/2 S 12:48 0:00 \_ sleep 61
saml 6251 0.0 0.0 115184 5320 pts/2 S 12:48 0:00 bash
saml 6272 0.0 0.0 100988 468 pts/2 S 12:48 0:00 \_ sleep 61
saml 6252 0.0 0.0 115184 5324 pts/2 S 12:48 0:00 bash
saml 6269 0.0 0.0 100988 464 pts/2 S 12:48 0:00 \_ sleep 61
...
...
Aはkillall bash
、彼らが手に負えなくなる前に、物事を停止します。この方法でクリーンアップを行うのは少し手間がかかる場合がありますが、すべてのbash
シェルを破壊する可能性のない優しい優しい方法は、次のようにすることです。
フォーク爆弾が実行される擬似端末を決定する
$ tty
/dev/pts/4
擬似端末を殺す
$ pkill -t pts/4
まあの各呼び出しbash
とsleep
C関数の呼び出しですfork()
からbash
コマンドが実行された場所からシェル。
bash
別の端末で実行されている可能性があります。使用する方が良いでしょうpkill -t pts/2
。