Почему эти бомбы-вилки работают по-разному, и каково значение & в этом?

16

Я понимаю, как работает обычная вилочная бомба, но я не совсем понимаю, почему & в конце обычной бомбы вилки требуется и почему эти сценарии ведут себя по-разному:

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

и

:(){ : | :& }; :

Первый вызывает всплеск использования процессора, а затем возвращает меня к экрану входа. Последнее вместо этого просто заставляет мою систему зависать, заставляя меня сильно перезагрузиться. Это почему? Оба постоянно создают новые процессы, так почему система ведет себя по-разному?

Оба сценария также ведут себя иначе, чем

:(){ : | : }; :

что не вызывает никаких проблем, даже если бы я ожидал, что они будут одинаковыми. Страница руководства bash утверждает, что команды в конвейере уже выполняются в подоболочке, поэтому я уверен, что: | : уже должно хватить. Я верю и должен просто запустить трубопровод в новой оболочке, но почему это так сильно меняется?

Редактировать: Используя htop и ограничивая количество процессов, я смог увидеть, что первый вариант создает реальное дерево процессов, второй вариант создает все процессы на одном уровне, а последний вариант, кажется, не создает никаких процессов. совсем. Это смущает меня еще больше, но, может быть, это как-то помогает?

Дэн К.
источник
2
я думаю, что в вашем последнем варианте пропущена точка с запятой::(){ : | :; }; :
adonis

Ответы:

22

ПРЕДУПРЕЖДЕНИЕ НЕ ПЫТАЙТЕСЬ ЗАПУСТИТЬ ЭТО НА ПРОИЗВОДСТВЕННОЙ МАШИНЕ. ПРОСТО НЕ. Предупреждение: чтобы попробовать любые «бомбы», убедитесь, что они ulimit -uиспользуются. Читайте ниже [а] .

Давайте определим функцию для получения 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выполняется, затем выводится «да», режим ожидания в течение 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 секунду, а затем приглашение возвращается.

Это причина того, что труба |запускает два процесса параллельно.

& фон

Если мы изменим вызов, добавив окончание &:

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

Обратите внимание, что мы не использовали ;в определении b(новые строки использовались для разделения элементов). Тем не менее, для определения в одной строке обычно используется ;, например:

bize:~$ b(){ bomb | bomb ; }

Большинство пробелов также не являются обязательными, мы можем написать эквивалент (но менее ясно):

bize:~$ b(){ bomb|bomb;}

Мы также можем использовать a &для разделения }(и отправки двух процессов в фоновый режим).

Бомба.

Если мы заставим функцию кусать свой хвост (вызывая себя), мы получим «ответную бомбу»:

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, когда список ошибок остановится, чтобы получить приглашение).

Причина, по которой это называется «бомба-вилка», заключается в том, что оболочка запускает под-оболочку путем разветвления работающей оболочки и последующего вызова exec () для разветвленного процесса с командой для запуска.

Труба будет "раскошелиться" на два новых процесса. Выполнение этого до бесконечности вызывает бомбу.
Или кролик, как его первоначально называли, потому что он так быстро размножается.


Сроки:

  1. :(){ (:) | (:) }; time :
    Прекращено
    реальное 0m45.627s

  2. :(){ : | :; }; time :
    Прекращено
    реальное 0m15.283s

  3. :(){ : | :& }; time :
    реальный 0m00.002 с
    Все еще работает


Ваши примеры:

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

    Где второе закрытие )отделяет, }является более сложной версией :(){ :|:;};:. В любом случае каждая команда в канале вызывается внутри вложенной оболочки. Который является эффектом ().

  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и избавление системы от большинства (не всех) бомб. Пожалуйста, не спрашивайте, какие еще работают, я не скажу. Но все же: довольно низкий, но на безопасной стороне, адаптироваться к вашей системе .
Это гарантирует, что «вилочная бомба» не разрушит вашу систему .

Дальнейшее чтение:

Сообщество
источник