为什么这些 bash fork 炸弹的工作方式不同,其中 & 的意义是什么?

Dan*_* K. 17 bash shell-script fork

我知道普通的叉子炸弹是如何工作的,但我真的不明白为什么需要普通 bash 叉子炸弹末尾的 & 以及为什么这些脚本的行为不同:

:(){ (:) | (:) }; :
Run Code Online (Sandbox Code Playgroud)

:(){ : | :& }; :
Run Code Online (Sandbox Code Playgroud)

前者在让我回到登录屏幕之前会导致 CPU 使用率飙升。后者只会导致我的系统冻结,迫使我硬重启。这是为什么?两者都在不断地创建新进程,那么为什么系统的行为不同呢?

这两个脚本的行为也与

:(){ : | : }; :
Run Code Online (Sandbox Code Playgroud)

这根本不会引起任何问题,即使我原以为它们是相似的。bash 手册页指出管道中的命令已经在子shell 中执行,所以我相信: | : 应该够了。我相信&应该只是在新的子shell中运行管道,但为什么会发生如此大的变化?

编辑:使用 htop 并限制进程数量,我能够看到第一个变体创建了一个实际的进程树,第二个变体在同一级别创建了所有进程,而最后一个变体似乎没有创建任何进程根本。这让我更加困惑,但也许它以某种方式有所帮助?

小智 22

警告不要试图在生产机器上运行它。只是不要。 警告:要尝试任何“炸弹”,请确保ulimit -u正在使用。阅读下文[a]

让我们定义一个函数来获取 PID 和日期(时间):

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

bomb新用户的一个简单的非问题功能(保护自己:阅读[a]):

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

当该函数被调用执行时,其工作原理如下:

bize:~$ bomb
  START 0002786 23:07:34
yes
    END 0002786 23:07:35
bize:~$
Run Code Online (Sandbox Code Playgroud)

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:~$
Run Code Online (Sandbox Code Playgroud)

两个命令同时启动,两个命令都会在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
Run Code Online (Sandbox Code Playgroud)

提示立即返回(所有操作都发送到后台)并且两个命令像以前一样执行。请注意[1]进程的PID之前打印的“作业编号”的值3380。稍后,将打印相同的数字以指示管道已结束:

[1]+  Done                    bomb | bomb
Run Code Online (Sandbox Code Playgroud)

这就是 的效果&

这就是&: 让进程更快启动的原因。

更简单的名字

我们可以创建一个简单的函数b来执行这两个命令。输入三行:

bize:~$ b(){
> bomb | bomb
> }
Run Code Online (Sandbox Code Playgroud)

并执行为:

bize:~$ b
  START 0003563 23:21:10
yes
  START 0003564 23:21:10
yes
    END 0003564 23:21:11
    END 0003563 23:21:11
Run Code Online (Sandbox Code Playgroud)

请注意,我们;b( 换行符用于分隔元素)的定义中使用了 no 。但是,对于一行定义,通常使用;,如下所示:

bize:~$ b(){ bomb | bomb ; }
Run Code Online (Sandbox Code Playgroud)

大多数空格也不是强制性的,我们可以写出等价的(但不太清楚):

bize:~$ b(){ bomb|bomb;}
Run Code Online (Sandbox Code Playgroud)

我们也可以使用a&来分隔}(并将两个进程发送到后台)。

炸弹。

如果我们让函数咬住它的尾巴(通过调用它自己),我们就会得到“叉炸弹”:

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

为了让它更快地调用更多函数,将管道发送到后台。

bize:~$ b(){ b|b&}       ### Usually written as b(){ b|b& }
Run Code Online (Sandbox Code Playgroud)

如果我们在一个 required 之后附加对函数的第一次调用;并将名称更改为:我们得到:

bize:~$ :(){ :|:&};:
Run Code Online (Sandbox Code Playgroud)

通常写成 :(){ :|:& }; :

或者,以有趣的方式写成,还有其他名字(雪人):

?(){ ?|?&};?
Run Code Online (Sandbox Code Playgroud)

ulimit(您应该在运行之前设置)将使提示在出现大量错误后很快返回(当错误列表停止时按回车键以获取提示)。

之所以将其称为“分叉炸弹”,是因为 shell 启动子 shell 的方式是分叉正在运行的 shell,然后使用要运行的命令将 exec() 调用到分叉的进程。

一个管道将“分叉”两个新进程。将其无限放大会导致炸弹。
或者最初被称为兔子,因为它繁殖得如此之快。


定时:

  1. :(){ (:) | (:) }; time :
    终止
    实数 0m45.627s

  2. :(){ : | :; }; time :
    终止
    实数 0m15.283s

  3. :(){ : | :& }; time :
    real 0m00.002 s
    仍在运行


你的例子:

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

    第二个关闭)分隔的地方}是更复杂的:(){ :|:;};:. 无论如何,管道中的每个命令都在子外壳内调用。这是 的效果()

  2. :(){ : | :& }; :

    是更快的版本,写入时没有空格::(){(:)|:&};:(13 个字符)。

  3. :(){ : | : }; : ### 在 zsh 中有效,但在 bash 中无效。

    有一个语法错误(在 bash 中),在结束之前需要一个元字符}
    如下所示:

     :(){ : | :; }; :
    
    Run Code Online (Sandbox Code Playgroud)

[a] 创建一个新的干净用户(我将称其为我的bize)。在控制台中登录到这个新用户sudo -i -u bize,或者:

$ su - bize
Password: 
bize:~$
Run Code Online (Sandbox Code Playgroud)

检查然后更改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
Run Code Online (Sandbox Code Playgroud)

仅使用 10 个作品,因为只有一个单独的新用户:bize。它可以更轻松地调用killall -u bize并使系统摆脱大多数(不是全部)炸弹。请不要问哪些仍然有效,我不会告诉。但仍然:相当低,但安全起见,适应您的系统
将确保“叉炸弹”不会使您的系统崩溃

进一步阅读: