Пройдя через знаменитые вопросы о Fork Bomb на Askubuntu и многих других сайтах Stack Exchange, я не совсем понимаю, что все говорят, как будто это очевидно.
Многие ответы ( лучший пример ) говорят так:
«
{:|: &}
означает запустить функцию:
и:
снова отправить ее вывод в функцию»
Ну, что именно на выходе :
? Что передается другому :
?
А также:
По сути, вы создаете функцию, которая вызывает себя дважды при каждом вызове и не имеет возможности завершить себя.
Как именно это выполняется дважды ? По моему мнению, ничто не передается второму :
до тех пор, пока первое :
не завершит его выполнение, которое фактически никогда не закончится.
В C
, например,
foo()
{
foo();
foo(); // never executed
}
второе foo()
вообще не выполняется, просто потому, что первое foo()
никогда не заканчивается.
Я думаю, что та же логика применима :(){ :|: & };:
и к
:(){ : & };:
выполняет ту же работу, что и
:(){ :|: & };:
Пожалуйста, помогите мне понять логику.
источник
:|:
-вторых:
, не нужно ждать завершения первого.Ответы:
Трубопровод не требует, чтобы первый экземпляр заканчивался до запуска другого. На самом деле, все это на самом деле делает это перенаправляет стандартный вывод первого экземпляра в стандартный ввод из второй, так что они могут работать одновременно ( так как они должны для вилочного бомбы на работу).
':' ничего не записывает в другой экземпляр ':', он просто перенаправляет стандартный вывод на стандартный ввод второго экземпляра. Если он что-то пишет во время своего выполнения (чего он никогда не сделает, так как он ничего не делает, кроме как разветвляется), он перейдет к стандартному входу другого экземпляра.
Это помогает представить stdin и stdout в виде кучи:
Все, что записано в stdin, будет готово, когда программа решит читать с него, в то время как stdout работает так же: куча, в которую вы можете писать, поэтому другие программы могут читать с нее, когда захотят.
Таким образом, легко представить себе ситуации, такие как канал, который не имеет связи (две пустые группы) или несинхронизированные операции записи и чтения.
Так как мы просто перенаправляем ввод и вывод экземпляров, не требуется, чтобы первый экземпляр заканчивался до запуска второго. Обычно желательно, чтобы оба запускались одновременно, чтобы второй мог работать с данными, которые анализируются первым на лету. Вот что происходит здесь, оба будут вызваны без необходимости ждать, пока первый закончит. Это относится ко всем цепочкам командных линий.
Первый из них не будет работать, потому что, хотя он работает сам по себе рекурсивно, функция вызывается в фоновом режиме (
: &
). Первый:
не ждет, пока «потомок»:
вернется, прежде чем завершится сам, так что в итоге у вас, вероятно, будет только один экземпляр:
выполнения. Если бы у вас было:(){ : };:
это, это сработало бы, так как первый:
будет ждать:
возвращения «ребенка» , который будет ждать возвращения своего «ребенка»:
, и так далее.Вот как будут выглядеть разные команды с точки зрения количества запущенных экземпляров:
:(){ : & };:
1 экземпляр (вызовы
:
и выходы) -> 1 экземпляр (вызовы:
и выходы) -> 1 экземпляр (вызовы:
и выходы) -> 1 экземпляр -> ...:(){ :|: &};:
1 экземпляр (вызов 2
:
и выход) -> 2 экземпляра (каждый вызов 2:
и выход) -> 4 экземпляра (каждый вызов 2:
и выход) -> 8 экземпляров -> ...:(){ : };:
1 экземпляр (вызывает
:
и ждет его возврата) -> 2 экземпляра (дочерний вызов другого:
и ждет его возврата) -> 3 экземпляра (дочерний вызов другого:
и ожидает его возврата) -> 4 экземпляра -> ...:(){ :|: };:
1 экземпляр (вызывает 2
:
и ждет их возврата) -> 3 экземпляра (дети вызывают 2:
и ждут, пока они вернутся) -> 7 экземпляров (дети вызывают 2:
и ждут, пока они вернутся) -> 15 экземпляров -> ...Как вы можете видеть, вызов функции в фоновом режиме (с использованием
&
) на самом деле замедляет форк-бомбу, потому что вызываемый будет выходить до того, как вызванные функции вернутся.источник
:(){ : & && : &}; :
также работать как вилка бомба? Вы также увеличите экспоненциально, и, на самом деле, вы можете добавить: &
туда несколько, чтобы увеличить их еще быстрее.bash: syntax error near unexpected token &&'
. Вы можете сделать это::(){ $(: &) && $(: &)}; :
Но, опять же, в отличие от конвейера, он не будет работать параллельно. Что будет эквивалентно:(){: & };:
. Как проверить? попробуйте этоtime $( $(sleep 1 & ) && $(sleep 1 &) )
и этоtime $(sleep 1 | sleep 1)
:(){ $(: &) && $(: &)};
это функция, которая выдает логическую операцию И в возвращаемых значениях первого и второго экземпляра. Проблема в том, что, поскольку логическое И истинно, только если оба значения истинны, для эффективности будет запущен только первый экземпляр. Если его возвращаемое значение равно 1, то запускается второй экземпляр. Если вы хотите сделать вилочную бомбу еще быстрее, я думаю, вы могли бы просто:(){ :|:|: &}; :
if [ prog1 ]; then; prog2; fi
) вы можете просто написать (prog1 && prog2
), и prog2 будет работать только в том случае, если возвращаемое значение prog1 было истинным.&&
для вызоваapt-get update && apt-get upgrade
, и&
в конце строки, чтобы работать в фоновом режиме, но это здорово, что они не будут работать вместе. Точка с запятой также не работает с амперсандом.