这个fork bomb
提取自此处:
:(){ :|:& };:
SA
经常用这个脚本来测试用户进程限额设置是否正确(通过配置/etc/security/limits.conf
和PAM
)。
今天试了一把,真把系统搞挂了,最后不得不由SA
重启机器-_-b
下面解析下这13
个char
:
:()
定义一个函数,名叫:
,无参。
{ :|:& };
:
的函数体。
:
调用该函数。
再看函数体部分,如果只是:
,那只是个简单的尾递归程序,如果bash
优化得当不会有任何问题。
再变复杂一些:
: => :|:
这样每递归一次,都会额外再创建2
个进程(到这里其实就已经会不断fork
,算是半个fork bomb
),A和B,且A的STDOUT
是B的STDIN
,且caller
会执行waitpid
等待这两个进程挂掉,且caller
挂掉会带走它们俩。
考虑第一次递归的情况,加上本身1
个,总共会有3
个进程,证明一下:
vi test
:
#!/bin/bash
a(){
a
};
b(){
b
};
a|b
./test
执行后,在另一个窗口ps -ef|grep test
,可以看到确实如此:
root 17001 16625 0 20:53 pts/0 00:00:00 /bin/bash ./test
root 17002 17001 99 20:53 pts/0 00:00:01 /bin/bash ./test
root 17003 17001 99 20:53 pts/0 00:00:01 /bin/bash ./test
还可以看到函数体中管道两边创建的进程的爹都是caller
: 17001
。
最后:
:|: => :|:&
加上&
是为了让爹死了之后自己还能活。
shell
命令如果没有&
,执行过程是:
shell
先fork
一个进程A,这个进程收到SIGHUP
信号时会退出。 execve
执行任务shell
执行waitpid
等待A挂掉。然后是带&
的情况:
shell
先fork
一个进程B,并执行execve
开始干活;该进程收到SIGHUP
时不会退出,只是改认init
(PID=1
)为爹。shell
不等待B挂掉继续干自己的活。所以:|:&
的净效果是,再fork
一个进程B,工作内容是:|:
,这时候caller
没活干了也不用等B挂,所以直接退出。B在做:|:
时又要再创建2
个进程干同样的活,且要等它们挂。
这样1
轮递归的效果是1
个进程变成3
个;到第2
轮时B创建的2
个进程会导致另外6
个进程被创建,然后第一轮的3
个都退出,还剩6
个;到第3
轮时,上一轮6
个进程中的4
个会导致另外12
个进程被创建,然后这6
个都退出,剩12
个;之后依次类推。
容易发现规律是1->3->6->12->24
,之后都是倍增,几何级数。且kill
一个爹最多带走2
个子,APM再高也来不及只好重启。
联系客服