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

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

Насколько это возможно , когда я выполняю файл в своей оболочке? Я имею в виду, если я ./somefile some arguments : ./somefile some arguments в мою оболочку и нажмите return (и somefile существует в cwd, и у меня есть права на чтение + выполнение для somefile ), то что происходит под капотом?

Я думал, что ответ был:

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

Однако кто-то сказал мне, что если файл является простым текстом, то оболочка пытается выполнить команды (как если бы я набрал bash somefile ). Я не верил в это, но я просто попробовал, и это было правильно. Поэтому у меня явно есть неправильные представления о том, что на самом деле происходит здесь, и хотелось бы понять механику.

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

4 Solutions collect form web for “Что именно происходит, когда я запускаю файл в своей оболочке?”

Окончательный ответ на вопрос «как запустить программы» в Linux – это пара статей на LWN.net под названием, на удивление достаточно, как запускаются программы и как программы запускаются: бинарные файлы ELF . В первой статье кратко рассматриваются сценарии. (Строго говоря, окончательный ответ находится в исходном коде, но эти статьи легче читать и предоставлять ссылки на исходный код.)

Небольшое экспериментирование показывает, что вы в значительной степени поняли это и что выполнение файла, содержащего простой список команд без shebang, должно обрабатываться оболочкой. Управляющая страница execve (2) содержит исходный код для тестовой программы, execve; мы будем использовать это, чтобы увидеть, что происходит без оболочки. Сначала напишите testcript, 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 трубопроводовcr2 на less :

 ./testscr2 | less -ppstree 

В моей системе я получаю

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

Как вы можете видеть, есть оболочка, которую я использовал, zsh , которая начиналась less , и вторая оболочка, простая sh ( dash в моей системе), чтобы запустить скрипт, который запускал pstree . В zsh это обрабатывается zexecve в Src/exec.c : оболочка использует execve(2) чтобы попытаться выполнить команду, и если это не удается, она считывает файл, чтобы увидеть, имеет ли он shebang, обрабатывая его соответственно (что ядро также выполнило бы), и если это не удастся, он пытается запустить файл с 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 failed не удалось, execve , установлен ли файл в установленном режиме. Если это так, и это не каталог, то выполните его содержимое как скрипт оболочки.

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

Отчасти это зависит от конкретной функции семейства exec которая используется. execve , как показал Стивен Китт , только запускает файлы в правильном двоичном формате или сценариях, которые начинаются с правильного shebang.

Однако execlp и execvp идут еще дальше: если shebang не был прав, файл выполняется с /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] (см. Ниже ОБОСНОВАНИЕ на [EINVAL]).

  3. Если файл образа процесса имеет соответствующие права, но не распознается иначе:

    1. Если это вызов execlp () или execvp (), они вызывают интерпретатор команд, предполагая, что файл образа процесса является сценарием оболочки.

    2. Если это не вызов execlp () или execvp (), возникает ошибка, а errno – [ENOEXEC].

Это не указывает, как получается интерпретатор команд, поэтому он не указывает, что должна быть указана ошибка. Поэтому я полагаю, что разработчики Linux разрешили запускать такие файлы с помощью /bin/sh (или это уже было обычной практикой, и они просто последовали этому примеру).

FWIW, man-страница FreeBSD для exec(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 .

Это может быть добавление к ответу Стивена Китта, как комментарий источника 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. 

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

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

  • sed и sudo с заменой строки
  • Если я нахожу «sudo» в начале одного лайнера, применим ли он к остальным командам?
  • Сценарий оболочки с циклом for и "array"
  • Как запустить команду на TTY из сеанса SSH?
  • Как установить вывод heredoc в локальную переменную
  • "Или" в оболочке glob
  • Каковы режимы readline, раскладки клавиш и их привязки по умолчанию?
  • Quotes exercise - как сделать ssh внутри ssh во время запуска sql внутри второго ssh?
  • zsh HISTFILE - все еще читается из ~ / .zsh_history
  • Почему соответствует строчным буквам в bash?
  • Ограничение размера файла превышено в bash
  • Linux и Unix - лучшая ОС в мире.