|管道

|管道

我了解普通的 fork 炸弹是如何工作的,但我不太明白为什么公共 bash fork 炸弹末尾需要 & 以及为什么这些脚本的行为不同:

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

:(){ : | :& }; :

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

这两个脚本的行为也不同

:(){ : | : }; :

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

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

答案1

警告请勿尝试在生产机器上运行此程序。只是不要。 警告:要尝试任何“炸弹”,请确保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:~$

两个命令同时启动,两个命令都会在 1 秒后结束,并且然后提示符返​​回。

|这就是 pipeline并行启动两个进程的原因。

& 背景

如果我们改变呼叫添加结尾&

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&来分隔}(并将两个进程发送到后台)。

炸弹。

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

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 键以获取提示)。

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

管道将“分叉”两个新进程。这样做到无穷大会导致炸弹。
或者最初被称为“兔子”,因为它繁殖得很快。


定时:

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

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

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


你的例子:

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

    第二个结尾将)分开的地方}是 的更复杂版本:(){ :|:;};:。无论如何,管道中的每个命令都会在子 shell 内调用。这就是 的效果()

  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并清除系统中的大多数(不是全部)炸弹。请不要问哪些仍然有效,我不会告诉。但仍然:相当低,但为了安全起见,请适应您的系统
将确保“叉子炸弹”不会崩溃您的系统

进一步阅读:

相关内容