Пожалуйста, объясните функцию exec () и ее семью - PullRequest
86 голосов
/ 17 ноября 2010

Что такое функция exec() и ее семейство? Почему используется эта функция и как она работает?

Пожалуйста, кто-нибудь объяснит эти функции.

Ответы [ 6 ]

224 голосов
/ 17 ноября 2010

Проще говоря, в UNIX у вас есть концепция процессов и программ. Процесс - это то, в чем выполняется программа.

Простая идея UNIX "модели исполнения" заключается в том, что вы можете выполнять две операции.

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

Вторым является exec(), который заменяет программу в текущем процессе новой программой.

Из этих двух простых операций можно построить всю модель выполнения UNIX.


Чтобы добавить больше деталей к вышесказанному:

Использование fork() и exec() иллюстрирует дух UNIX в том смысле, что он обеспечивает очень простой способ запуска новых процессов.

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

Новый процесс (называемый дочерним) получает другой идентификатор процесса (PID) и имеет PID старого процесса (родительского) в качестве родительского PID (PPID).

Поскольку два процесса в настоящее время работают с одинаковым кодом, они должны иметь возможность определить, какой именно - код возврата fork() предоставляет эту информацию - дочерний элемент получает 0, родительский получает PID дочернего элемента. (в случае сбоя fork() дочерний элемент не создается и родитель получает код ошибки). Таким образом, родительский объект знает PID дочернего элемента и может общаться с ним, уничтожать его, ждать его и т. Д. (Дочерний процесс всегда может найти свой родительский процесс с помощью вызова getppid()).

Вызов exec() заменяет все текущее содержимое процесса новой программой. Он загружает программу в текущее пространство процесса и запускает ее из точки входа.

Итак, fork() и exec() часто используются последовательно, чтобы новая программа работала как дочерний элемент текущего процесса. Оболочки обычно делают это всякий раз, когда вы пытаетесь запустить такую ​​программу, как find - оболочка разветвляется, затем дочерний элемент загружает в память программу find, настраивая все аргументы командной строки, стандартный ввод-вывод и т. Д.

Но их не обязательно использовать вместе. Для программы вполне допустимо вызывать fork() без следующего exec(), если, например, программа содержит как родительский, так и дочерний код (вам нужно быть осторожным в том, что вы делаете, у каждой реализации могут быть ограничения). Это использовалось довольно много (и все еще используется) для демонов, которые просто прослушивают порт TCP и разрабатывают свою копию для обработки определенного запроса, в то время как родитель возвращается к прослушиванию. Для этой ситуации программа содержит как родительский , так и дочерний код.

Аналогичным образом, программы, которые знают, что они закончили и просто хотят запустить другую программу, не нуждаются в fork(), exec(), а затем wait()/waitpid() для ребенка. Они могут просто загрузить дочерний элемент прямо в текущее пространство процесса с помощью exec().

Некоторые реализации UNIX имеют оптимизированный fork(), который использует то, что они называют копированием при записи. Это хитрость, чтобы отложить копирование пространства процесса в fork() до тех пор, пока программа не попытается что-то изменить в этом пространстве. Это полезно для тех программ, которые используют только fork(), а не exec(), поскольку им не нужно копировать все пространство процесса. В Linux fork() создает только копии таблиц страниц и новую структуру задач, exec() выполняет основную работу по «разделению» памяти двух процессов.

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

В Linux также есть vfork(), еще более оптимизированный, который разделяет примерно все между двумя процессами. Из-за этого существуют определенные ограничения в том, что может делать ребенок, и родитель останавливается, пока ребенок не наберет exec() или _exit().

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

Обратите внимание, что существует целое семейство exec вызовов (execl, execle, execve и т. Д.), Но exec в контексте здесь означает любой из них.

Следующая диаграмма иллюстрирует типичную операцию fork/exec, в которой оболочка bash используется для отображения каталога с помощью команды ls:

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V
28 голосов
/ 01 июня 2016

Функции в семействе exec () имеют различное поведение:

  • l: аргументы передаются в виде списка строк в main ()
  • v: аргументы передаются как массив строк в main ()
  • p: путь / с для поиска новой работающей программы
  • e: среда может быть указана вызывающей стороной

Вы можете смешивать их, поэтому у вас есть:

  • int execl (const char * path, const char * arg, ...);
  • int execlp (const char * file, const char * arg, ...);
  • int execle (const char * path, const char * arg, ..., char * const envp []);
  • int execv (const char * path, char * const argv []);
  • int execvp (const char * file, char * const argv []);
  • int execvpe (const char * file, char * const argv [], char * const envp []);

Для всех них начальным аргументом является имя файла, который должен быть выполнен.

Для получения дополнительной информации прочитайте exec (3) man page :

man 3 exec  # if you are running a UNIX system
16 голосов
/ 17 ноября 2010

Семейство функций exec заставляет ваш процесс выполнять другую программу, заменяя старую программу, в которой он работал. Т.е., если вы позвоните

execl("/bin/ls", "ls", NULL);

затем программа ls выполняется с идентификатором процесса, текущим рабочим каталогом и пользователем / группой (права доступа) процесса, который вызвал execl. После этого оригинальная программа больше не работает.

Для запуска нового процесса используется системный вызов fork. Чтобы выполнить программу без замены оригинала, вам нужно fork, затем exec.

7 голосов
/ 18 ноября 2010

exec часто используется в сочетании с fork, о котором я видел, что вы также спрашивали, поэтому я буду обсуждать это с учетом этого.

exec превращает текущий процесс в другую программу. Если вы когда-нибудь смотрели «Доктор Кто», то это похоже на то, как он восстанавливается - его старое тело заменяется новым.

То, как это происходит с вашей программой и exec, заключается в том, что множество ресурсов, которые ядро ​​ОС проверяет, является ли файл, передаваемый в exec в качестве аргумента программы (первый аргумент), выполняется текущий пользователь (идентификатор пользователя процесса, выполняющего вызов exec), и если это так, он заменяет отображение виртуальной памяти текущего процесса на виртуальную память новым процессом и копирует данные argv и envp, которые были переданы в exec вызов в область этой новой карты виртуальной памяти. Здесь также может произойти несколько других вещей, но файлы, которые были открыты для программы с именем exec, будут по-прежнему открыты для новой программы, и у них будет такой же идентификатор процесса, но программа с именем exec прекратит работу ( если не удалось выполнить exec).

Причина, по которой это делается таким образом, заключается в том, что, разделив работающую новую программу на два шага, как это вы можете сделать некоторые вещи между двумя шагами. Самое распространенное, что нужно сделать, - убедиться, что в новой программе определенные файлы открыты как определенные файловые дескрипторы. (помните, что файловые дескрипторы не совпадают с FILE *, но являются значениями int, о которых знает ядро). Делать это вы можете:

int X = open("./output_file.txt", O_WRONLY);

pid_t fk = fork();
if (!fk) { /* in child */
    dup2(X, 1); /* fd 1 is standard output,
                   so this makes standard out refer to the same file as X  */
    close(X);

    /* I'm using execl here rather than exec because
       it's easier to type the arguments. */
    execl("/bin/echo", "/bin/echo", "hello world");
    _exit(127); /* should not get here */
} else if (fk == -1) {
    /* An error happened and you should do something about it. */
    perror("fork"); /* print an error message */
}
close(X); /* The parent doesn't need this anymore */

Это завершает бег:

/bin/echo "hello world" > ./output_file.txt

из командной оболочки.

5 голосов
/ 17 ноября 2010

что такое функция exec и ее семейство.

Семейство функций exec - это все функции, используемые для выполнения файла, такие как execl, execlp, execle, execv и execvp. Все они являются внешними интерфейсами для execve и предоставляют различные способы его вызова.

Почему эта функция используется

Exec функции используются, когда вы хотите выполнить (запустить) файл (программу).

и как это работает.

Они работают, перезаписывая текущий процессизображение с тем, что вы запустили.Они заменяют (завершая) текущий запущенный процесс (вызвавший команду exec) новым запущенным процессом.

Для получения более подробной информации: см. Эту ссылку .

4 голосов
/ 17 ноября 2010

Функции exec(3,3p) заменяют текущего процесса другим. То есть текущий процесс останавливается , а вместо него запускается другой, забирая некоторые ресурсы, которые были у исходной программы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...