Що саме відбувається, коли я виконую файл у своїй оболонці?


32

Отже, я подумав, що я добре розумію це, але просто пройшов тест (у відповідь на розмову, де я з кимось не погодився) і виявив, що моє розуміння є хибним ...

Як можна детальніше, що саме відбувається, коли я виконую файл у своїй оболонці? Що я маю на увазі, якщо я набираю : ./somefile some argumentsу свою оболонку і натискаю return (і somefileіснує в cwd, і я читав + виконувати дозволи somefile), то що відбувається під кришкою?

Я думав, що відповідь:

  1. Оболонка здійснює систематичний виклик exec, проходячи шлях доsomefile
  2. Ядро вивчає somefileі розглядає магічне число файлу, щоб визначити, чи це формат, з яким може оброблятися процесор
  3. Якщо магічне число вказує на те, що файл знаходиться у форматі, який може виконувати процесор, то
    1. створюється новий процес (із записом у таблиці процесів)
    2. somefileчитається / відображається в пам'яті. Створюється стек і виконання переходить до точки входу коду somefile, з ARGVініціалізацією до масиву параметрів (a char**, ["some","arguments"])
  4. Якщо магічне число - це шебанг, то exec()породжується новий процес, як зазначено вище, але використовуваний виконуваний файл - інтерпретатор, на який посилається шебанг (наприклад, /bin/bashабо /bin/perl), і somefileпередається вSTDIN
  5. Якщо файл не має дійсного магічного номера, виникає помилка типу "недійсний файл (неправильне магічне число): помилка формату Exec"

Однак хтось сказав мені, що якщо файл - це звичайний текст, то оболонка намагається виконувати команди (як би я набрала bash somefile). Я не повірив цьому, але просто спробував це, і це було правильно. Тож я явно маю деякі помилки щодо того, що насправді відбувається тут, і я хотів би зрозуміти механіку.

Що саме відбувається, коли я виконую файл у своїй оболонці? (якомога більш детально ...)


Немає ідеальної заміни для перегляду вихідного коду для повної глибини розуміння.
Wildcard

1
@Wildcard - це те, що я зараз роблю, насправді :-) Якщо зможу, я відповім на власне питання
Джош

1
source somefile./somefileХоча це сильно відрізняється від нового процесу, від якого він розщеплений .
триг

@thrig так, я згоден. Але я не думав, що ./somefileце призведе до того, що bash виконує команди, somefileякщо у файлу не було магічного числа. Я думав, що це просто відобразить помилку, і натомість це здається ефективноsource somefile
Джош

Я знову помиляюся, можу підтвердити, що якщо somefileце текстовий файл, то з'являється нова оболонка, якщо я спробую виконати її. Файл echo $$поводиться інакше, якщо я виконую його проти джерела.
Джош

Відповіді:


31

Остаточну відповідь на питання «як програми отримують працювати» на Linux є пара статей по LWN.net титулованих, що дивно, як програми отримують працювати і як програми отримують працювати: ELF бінарних файлів . У першій статті стисло розглядаються сценарії. (Строго кажучи, остаточна відповідь є у вихідному коді, але ці статті легше читати та надають посилання на вихідний код.)

Невеликий експеримент показує, що ви майже все правильно зрозуміли і що виконання файлу, що містить простий список команд, без шебанга, має обробляти оболонку. Сторінка execve (2) містить вихідний код для тестової програми, execve; ми використаємо це, щоб побачити, що відбувається без оболонки. Спочатку напишіть тестовий сценарій testscr1, що містить

#!/bin/sh

pstree

і інший testscr2, що містить тільки

pstree

Зробіть їх обома виконуваними та переконайтеся, що вони обидва запущені з оболонки:

chmod u+x testscr[12]
./testscr1 | less
./testscr2 | less

Тепер спробуйте ще раз, використовуючи execve(припустимо, що ви вбудували його в поточний каталог):

./execve ./testscr1
./execve ./testscr2

testscr1все ще працює, але testscr2виробляє

execve: Exec format error

Це показує, що оболонка справляється по- testscr2різному. Хоча він сам не обробляє сценарій, він все ще використовує /bin/shдля цього; це можна перевірити, перейшовши testscr2на less:

./testscr2 | less -ppstree

У моїй системі я потрапляю

    |-gnome-terminal--+-4*[zsh]
    |                 |-zsh-+-less
    |                 |     `-sh---pstree

Як бачите, є оболонка, яку я використовував, zshяка запустилася less, і друга оболонка, звичайна sh( dashу моїй системі), для запуску сценарію, який запускався pstree. У zshцьому обробляється zexecveв Src/exec.c: Оболонка використовує , execve(2)щоб спробувати виконати команду, і якщо це не вдається, він читає файл , щоб побачити , якщо він має хатину, обробляючи його відповідним чином (що ядро буде також зроблено), і якщо це не вдалося спробувати запустити файл sh, доки він не прочитав жодного нульового байта з файлу:

        for (t0 = 0; t0 != ct; t0++)
            if (!execvebuf[t0])
                break;
        if (t0 == ct) {
            argv[-1] = "sh";
            winch_unblock();
            execve("/bin/sh", argv - 1, newenvp);
        }

bashмає таку саму поведінку, реалізовану в корисномуexecute_cmd.c коментарі (як вказував taliezin ):

Виконайте просту команду, яка, сподіваємось, десь визначена у файлі диска.

  1. fork ()
  2. з'єднують труби
  3. шукати команду
  4. робити переадресації
  5. execve ()
  6. Якщо execveне вдалося, подивіться, чи встановлений файл у виконаному режимі. Якщо це так, і це не каталог, виконайте його вміст як скрипт оболонки.

POSIX визначає набір функцій, відомий як в exec(3)функції , яка обгортання execve(2)і забезпечити цю функціональність теж; див. відповідь Муру для детальної інформації. У Linux принаймні ці функції реалізуються бібліотекою С, а не ядром.


Це фантастично і містить деталі, які я шукав, дякую!
Джош

12

Частково це залежить від конкретної execсімейної функції, яка використовується. execve, як детально показав Стівен Кітт , запускає файли лише у правильному бінарному форматі або скриптах, які починаються з належного шебангу.

Тим НЕ менше , execlpі execvpзробити ще один крок вперед: якщо притон неправильно, файл виконується з /bin/shна Linux. Від man 3 exec:

Special semantics for execlp() and execvp()
   The execlp(), execvp(), and execvpe() functions duplicate the actions
   of the shell in searching for an executable file if the specified
   filename does not contain a slash (/) character.
   …

   If the header of a file isn't recognized (the attempted execve(2)
   failed with the error ENOEXEC), these functions will execute the
   shell (/bin/sh) with the path of the file as its first argument.  (If
   this attempt fails, no further searching is done.)

Це дещо підтримується POSIX (моє наголос):

Одне потенційне джерело плутанини, яке відзначають стандартні розробники, полягає в тому, як вміст файлу зображення процесу впливає на поведінку сімейства функцій exec. Далі опис вжитих дій:

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

  2. Якщо файл зображення процесу має відповідні привілеї та знаходиться у форматі, який виконується, але не є дійсним для цієї системи (наприклад, визнаний бінарний файл для іншої архітектури), це помилка, і помилка errno встановлюється на [EINVAL] (див. Пізніше RATIONALE на [EINVAL]).

  3. Якщо файл зображення процесу має відповідні привілеї, але іншим чином не розпізнається:

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

    2. Якщо це не виклик до execlp () або execvp (), то виникає помилка, і errno встановлюється на [ENOEXEC].

Це не вказує, як отримується інтерпретатор команд, але не вказує, що помилка повинна бути надана. Тому я здогадуюсь, що Linux-розробники дозволили запускати такі файли /bin/sh(або це вже було звичайною практикою, і вони просто дотримувались відповідності).

FWIW, сторінка FreeBSDexec(3) також згадує подібну поведінку:

 Some of these functions have special semantics.

 The functions execlp(), execvp(), and execvP() will duplicate the actions
 of the shell in searching for an executable file if the specified file
 name does not contain a slash ``/'' character. 
 …
 If the header of a file is not recognized (the attempted execve()
 returned ENOEXEC), these functions will execute the shell with the path
 of the file as its first argument.  (If this attempt fails, no further
 searching is done.)

AFAICT, однак, жодна звичайна оболонка не використовує execlpабо execvpбезпосередньо, імовірно, для більш тонкого контролю над навколишнім середовищем. Всі вони реалізують ту саму логіку, використовуючи execve.


3
Я б також додати , що , по крайней мере на Linux, execl, execlp, execle, execv, execvpі execvpeвсе передні кінці до системного execveвиклику; перші надаються бібліотекою С, ядро ​​про це знає execveexecveatнині).
Стівен Кітт

@StephenKitt Це пояснює, чому я не зміг знайти сторінку для цих функцій у розділі
man7.org

6

Це може бути доповненням до відповіді Стівена Кітта, як коментар з bashджерела у файлі execute_cmd.c:

Виконайте просту команду, яка, сподіваємось, десь визначена у файлі диска.

1. fork ()
2. connect pipes
3. look up the command
4. do redirections
5. execve ()
6. If the execve failed, see if the file has executable mode set.  

Якщо це так, і це не каталог, виконайте його вміст як скрипт оболонки.


0

Він виконується як сценарій оболонки, він не отримується (наприклад, змінні, встановлені у виконаному файлі, не впливають назовні). Напевно, неприємно від туманного минулого, коли була одна оболонка та один виконуваний формат. Не виконуваний файл, це повинен бути сценарій оболонки.


2
Ви неправильно зрозуміли моє запитання. Що докладно відбувається ? Як мінімум, мені потрібно зрозуміти, що перевіряє на шебанг, це exec()чи оболонка? Я хочу значно більше внутрішніх
Джош
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.