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() 调用到分叉的进程。
一个管道将“分叉”两个新进程。将其无限放大会导致炸弹。
或者最初被称为兔子,因为它繁殖得如此之快。
:(){ (:) | (:) }; time :
终止
实数 0m45.627s
:(){ : | :; }; time :
终止
实数 0m15.283s
:(){ : | :& }; time :
real 0m00.002 s
仍在运行
:(){ (:) | (:) }; :
第二个关闭)分隔的地方}是更复杂的:(){ :|:;};:. 无论如何,管道中的每个命令都在子外壳内调用。这是 的效果()。
:(){ : | :& }; :
是更快的版本,写入时没有空格::(){(:)|:&};:(13 个字符)。
:(){ : | : }; : ### 在 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并使系统摆脱大多数(不是全部)炸弹。请不要问哪些仍然有效,我不会告诉。但仍然:相当低,但安全起见,适应您的系统。
这将确保“叉炸弹”不会使您的系统崩溃。
进一步阅读: