Jako výsledek potrubí v x | y
, je vytvořen podshell, který obsahuje potrubí jako součást skupiny procesů v popředí. Toto pokračuje ve vytváření podskořápek (přes fork()
) na dobu neurčitou, čímž vznikne vidlicová bomba.
$ 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
K rozvětvení ve skutečnosti nedojde, dokud není kód spuštěn, což je poslední vyvolání :
ve vašem kódu.
Jak rozebrat, jak funguje vidlicová bomba:
:()
- definovat novou funkci nazvanou:
{ :|: & }
- definice funkce, která rekurzivně převádí volající funkci do jiné instance volající funkce na pozadí:
- zavolejte funkci vidlicové bomby
To obvykle není příliš náročné na paměť, ale bude to vysávat PID a spotřebovávat cykly CPU.
Poslední bit kódu, ;:
je spuštěna funkce :(){ ... }
. Zde dochází k rozvětvení.
Středník ukončuje první příkaz a začínáme další, tj. vyvolání funkce :
. Definice této funkce zahrnuje volání sebe sama (:
) a výstup tohoto volání je přesměrován do verze na pozadí :
. To podporuje proces na neurčito.
Pokaždé, když voláte funkci :()
voláte funkci C fork()
. Nakonec to vyčerpá všechna ID procesů (PID) v systému.
Příklad
Můžete vyměnit |:&
s něčím jiným, abyste si mohli udělat představu o tom, co se děje.
Nastavení hlídače
V jednom okně terminálu proveďte toto:
$ watch "ps -eaf|grep \"[s]leep 61\""
Nastavte vidlicovou bombu se zpožděnou pojistkou
V dalším okně spustíme mírně upravenou verzi vidlicové bomby. Tato verze se pokusí udusit sama sebe, abychom mohli studovat, co dělá. Naše verze bude 61 sekund spát, než zavolá funkci :()
.
Po vyvolání provedeme také počáteční volání. Ctrl + z a poté zadejte bg
.
$ :(){ sleep 61; : | : & };:
# control + z
[1]+ Stopped sleep 61
[2] 5845
$ bg
[1]+ sleep 61 &
Nyní, když spustíme jobs
v úvodním okně uvidíme toto:
$ jobs
[1]- Running sleep 61 &
[2]+ Running : | : &
Po několika minutách:
$ jobs
[1]- Done sleep 61
[2]+ Done : | :
Přihlaste se s pozorovatelem
Mezitím v druhém okně, kde spouštíme 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
Hierarchie procesů
A ps -auxf
ukazuje tuto hierarchii procesu:
$ 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
...
...
Čas čištění
A killall bash
zastaví věci, než se vymknou z rukou. Provádět úklid tímto způsobem může být trochu náročné, je to laskavější a šetrnější způsob, který nebude potenciálně trhat každou bash
shell down, by bylo udělat následující:
-
Určete, v jakém pseudoterminálu poběží vidlicová bomba
$ tty /dev/pts/4
-
Zabijte pseudoterminál
$ pkill -t pts/4
Co se tedy děje?
No každé vyvolání bash
a sleep
je volání funkce C fork()
z bash
shell, odkud byl příkaz spuštěn.