Что именно происходит, когда я запускаю файл в моей оболочке?

32

Итак, я подумал, что хорошо понимаю это, но просто провел тест (в ответ на разговор, в котором я с кем-то не согласен) и обнаружил, что мое понимание неверно ...

Как можно более подробно, что именно происходит, когда я запускаю файл в моей оболочке? Что я имею в виду, если я ввожу : ./somefile some argumentsв свою оболочку и somefileнажимаю return (и существует в 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). Я не верил в это, но я просто попробовал, и это было правильно. Поэтому у меня явно есть некоторые неправильные представления о том, что на самом деле здесь происходит, и я хотел бы понять механику.

Что именно происходит, когда я запускаю файл в моей оболочке? (насколько подробно это разумно ...)

мистифицировать
источник
1
Там нет идеальной замены для просмотра исходного кода для полной глубины понимания.
Уайлдкарт
1
@Wildcard это то, что я делаю прямо сейчас, на самом деле :-) Если я могу, я отвечу на свой вопрос
Josh
1
source somefileочень сильно отличается от нового процесса, который был отменен ./somefile.
thrig
@ Да, я согласен. Но я не думал, что ./somefileэто приведет к тому, что bash выполнит команды, somefileесли в файле нет магического числа. Я думал, что это просто отобразит ошибку, и вместо этого, по-видимому, эффективноsource somefile
Josh
Я снова ошибаюсь, я могу подтвердить, что если somefileтекстовый файл, то новая оболочка появляется, если я пытаюсь выполнить его. Файл echo $$ведет себя по-разному, если я выполняю его против исходного кода.
Джош

Ответы:

32

Окончательный ответ на вопрос «как программы запускаются» в 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с помощью полезного комментария (как указал талиезин ):

Выполните простую команду, которая, мы надеемся, где-то определена в файле на диске.

  1. fork ()
  2. соединить трубы
  3. искать команду
  4. делать перенаправления
  5. execve ()
  6. Если произошел execveсбой, посмотрите, установлен ли для файла режим запуска. Если это так, и это не каталог, то выполните его содержимое в виде сценария оболочки.

POSIX определяет набор функций, известный как в exec(3)функции , которая обертывание execve(2)и обеспечить эту функциональность тоже; подробности смотрите в ответе Муру . В Linux по крайней мере эти функции реализуются библиотекой C, а не ядром.

Стивен Китт
источник
Это фантастика и есть детали, которые я искал, спасибо!
Джош
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, страницаexec(3) руководства FreeBSD для также упоминает похожее поведение:

 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вызову; первые предоставляются библиотекой C, о которой ядро ​​знает только execve(и в execveatнастоящее время).
Стивен Китт
@StephenKitt Это объясняет, почему я не смог найти man-страницу для этих функций в разделе 2.
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.  

Если это так, и это не каталог, то выполните его содержимое в виде сценария оболочки.

taliezin
источник
0

Он выполняется как сценарий оболочки, он не является источником (например, переменные, установленные в исполняемом файле, не влияют снаружи). Вероятно, рудимент из туманного прошлого, когда была одна оболочка и один исполняемый формат. Не исполняемый файл, это должен быть скрипт оболочки.

vonbrand
источник
2
Вы неправильно поняли мой вопрос. Что происходит в деталях? Как минимум, мне нужно понять, что проверяет шебанг, это exec()или оболочка? Я хочу значительно больше внутренних
органов