web-dev-qa-db-ja.com

フォーク爆弾のfork()はどこにありますか:(){:|:&};:?

警告:ほとんどのシェルでこのコマンドを実行すると、システムの破損につながり、修正するには強制シャットダウンが必要になります

再帰関数:(){ :|: & };:とその機能を理解しています。しかし、forkシステムコールがどこにあるのかわかりません。よくわかりませんが、パイプの中で疑わしいです|

26
mavillan

_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サイクルを消費します。

31
Chris Down

コードの最後のビット_;:_は、関数:(){ ... }を実行しています。ここでフォークが発生しています。

セミコロンは最初のコマンドを終了し、次のコマンドを開始します。つまり、関数_:_を呼び出します。この関数の定義には、それ自体への呼び出し(_:_)が含まれており、この呼び出しの出力は、バックグラウンドバージョン_:_にパイプされます。これは無期限にプロセスを支えます。

関数:()を呼び出すたびに、C関数fork()を呼び出します。最終的に、これはシステム上のすべてのプロセスID(PID)を使い果たします。

_|:&_を他の何かと交換して、何が起こっているのかを知ることができます。

ウォッチャーを設定する

1つのターミナルウィンドウで次のようにします。

_$ watch "ps -eaf|grep \"[s]leep 61\""
_

「ヒューズ遅延」フォーク爆弾をセットアップする

別のウィンドウで、フォーク爆弾のわずかに変更されたバージョンを実行します。このバージョンはそれ自体がスロットルすることを試みるので、それが何をしているのか研究することができます。このバージョンは、関数:()を呼び出す前に61秒間スリープします。

また、呼び出された後の最初の呼び出しもバックグラウンドで処理します。 Ctrlz、次に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
...
...
_

片付け時間

_killall bash_は、手に負えなくなる前に停止します。この方法でクリーンアップを行うと、多少の手間がかかる可能性があり、bashシェルをすべて破壊しない可能性のある、より穏やかな方法は、次のようにすることです。

  1. フォーク爆弾が実行される予定の疑似端末を特定する

    _$ tty
    /dev/pts/4
    _
  2. 疑似端末を強制終了します

    _$ pkill -t pts/4
    _

どうしたの?

bashsleepを呼び出すたびに、コマンドが実行されたbashシェルからC関数fork()が呼び出されます。

24
slm