Як саме типова оболонка «вилка бомба» називає себе двічі?


15

Переглянувши відомі питання Fork Bomb на Askubuntu та багатьох інших сайтах Stack Exchange, я не зовсім розумію, що всі говорять, як це очевидно.

Багато відповідей ( найкращий приклад ) говорять про це:

" {:|: &}означає запустити функцію :і знову надіслати її результат :функції"

Ну, що саме є результатом :? Що передається іншому :?

І також:

По суті, ви створюєте функцію, яка дзвонить собі двічі кожен виклик і не має жодного способу самозавершити себе.

Як саме це виконується двічі ? На мою думку, до другого нічого не передається, :поки перший не :закінчить своє виконання, що насправді ніколи не закінчиться.

В C, наприклад,

foo()
{
    foo();
    foo(); // never executed 
}

другий foo()взагалі не виконується, лише тому, що перший foo()ніколи не закінчується.

Я думаю, що та ж логіка стосується і цього :(){ :|: & };: і

:(){ : & };:

виконує таку ж роботу, як і

:(){ :|: & };:

Будь ласка, допоможіть мені зрозуміти логіку.


9
Команда в трубопроводах працює паралельно, у :|:другому :не потрібно чекати, коли перший завершиться.
cuonglm

Відповіді:


26

Трубопровід не вимагає, щоб перша інстанція закінчувалася до початку другої. Насправді, все, що вона насправді робить, - це перенаправлення відтінку першої інстанції до stdin другого, щоб вони могли працювати одночасно (як це потрібно, щоб вилка бомба працювала).

Ну, що саме є результатом :? що передається іншому: ?

":" нічого не пише іншому ":", це просто перенаправлення stdout до stdin другої інстанції. Якщо він пише щось під час його виконання (чого ніколи не буде, оскільки нічого не робить, окрім себе роздвоєння), він би перейшов до стандартне введення іншої інстанції.

Це допомагає уявити 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 екземплярів -> ...

Як бачимо, виклик функції у фоновому режимі (за допомогою &) насправді уповільнює вилкову бомбу, оскільки виклик вийде, перш ніж викликані функції повернуться.


Питання. Буде :(){ : & && : &}; :також працювати як вилка бомба? Ви також збільшуватимете експоненціально, і, насправді, ви можете помістити туди множини : &, щоб збільшити його ще швидше.
JFA

@JFA `─> $: () {: & &:: &}; : `дає синтаксичну помилку bash: syntax error near unexpected token &&' . Ви можете зробити це : :(){ $(: &) && $(: &)}; :, Але знову ж таки, на відміну від конвеєра, він не буде працювати паралельно. Що було б рівнозначно :(){: & };:. Любите перевірити? спробуйте це time $( $(sleep 1 & ) && $(sleep 1 &) )і цеtime $(sleep 1 | sleep 1)
Severus Tux

Точно так, :(){ $(: &) && $(: &)};це функція, яка видає логічну операцію AND у повернених значеннях першої та другої інстанцій. Проблема полягає в тому, що логічний AND істинний, тільки якщо обидва значення є істинними, для ефективності буде запускатися лише перший екземпляр. Якщо його повернене значення дорівнює 1, то другий екземпляр запуститься. Якщо ви хочете зробити вилкову бомбу ще швидшою, я думаю, ви могли б просто :(){ :|:|: &}; :
надіти

Як бічна примітка, багато сценаріїв використовують таку поведінку AND у наступній ситуації: Скажіть, ви хочете запустити prog2, якщо prog1 повернеться true (що в bash дорівнює 0). Замість того, щоб робити if-оператор ( if [ prog1 ]; then; prog2; fi), ви могли просто написати ( prog1 && prog2), а prog2 запустився б лише, якщо значення повернення prog1 було правдивим.
Янц

Гаразд, це все чудові моменти. Я використовую &&дзвінки apt-get update && apt-get upgradeі &в кінці лінії, щоб працювати у фоновому режимі, але це чудово, що вони не працюватимуть разом. Точка з комою також не працює з символом "ampersand".
JFA
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.