Почему оболочки называют ветвление ()?

Когда процесс запускается с оболочки, почему оболочка разветвляет себя прежде, чем выполнить процесс?

Например, когда вводы данных пользователем grep blabla foo, почему оболочка не может просто звонить exec() на grep без дочерней оболочки?

Кроме того, когда оболочка разветвляется сама в эмуляторе терминала GUI, она запускает другой эмулятор терминала? (такой как pts/13 запуск pts/14)

32
задан 3 March 2014 в 03:49

4 ответа

Когда Вы звоните exec метод семьи, он не создает новый процесс, вместо этого exec замены память текущего процесса и система команд и т.д. с процессом, который Вы хотите выполнить.

Как пример, Вы хотите работать grep должностное лицо использования. bash процесс (который имеет отдельную память, адресное пространство). Теперь, когда Вы звоните exec(grep), должностное лицо заменит память текущего процесса, адресное пространство, система команд и т.д. с grep's данные. Это означает bash, процесс больше не будет существовать. В результате Вы не можете возвратиться к терминалу после завершения эти grep команда. Вот почему исполнительные методы семьи никогда не возвращаются. Вы не можете выполнить код после должностного лица; это недостижимо.

34
ответ дан 16 November 2019 в 11:15

Согласно эти pts, проверьте его сами: в оболочке, выполненной

echo $ 

для знания идентификатора процесса (PID), я имею, например

echo $
29296

Тогда выполненный, например sleep 60 и затем в другом терминале

(0)samsung-romano:~% ps -edao pid,ppid,tty,command | grep 29296 | grep -v grep
29296  2343 pts/11   zsh
29499 29296 pts/11   sleep 60

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

3
ответ дан 16 November 2019 в 11:15

Для каждой команды (пример: grep), что Вы выходите на подсказке удара, Вы на самом деле, намереваются запустить новый процесс и затем возвратиться для избиения подсказки после выполнения.

, Если процесс оболочки (удар) вызывает должностное лицо () для выполнения grep, процесс оболочки будет заменен grep. Grep будет хорошо работать, но после выполнения, управление не может возвратиться к оболочке, потому что процесс удара уже заменяется.

поэтому удар называет ветвление (), который не заменяет текущий процесс.

1
ответ дан 16 November 2019 в 11:15

TL; DR: Поскольку это - оптимальный метод для создания новых процессов и удержания контроль в интерактивной оболочке

ветвление () необходимо для процессов и каналов

Ответить на определенную часть этого вопроса, если grep blabla foo через нужно было назвать exec() непосредственно в родителе, родитель захватил бы для существования, и его PID со всеми ресурсами будет принят grep blabla foo.

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

Во-вторых, насколько интерактивные оболочки идут, Вы не можете выполнить внешнюю команду без разветвления. Запустить исполняемый файл, который живет на диске (например, /bin/df -h ), необходимо назвать один из exec() функции семейства, такой как execve(), который заменит родителя новым процессом, принять его PID и существующие дескрипторы файлов, и т.д. Для интерактивной оболочки, Вы хотите, чтобы управление возвратилось к пользователю и позволило родительской интерактивной оболочке продолжиться. Таким образом лучший способ состоит в том, чтобы создать подпроцесс через fork(), и позвольте тому процессу быть принятым через execve(). Так интерактивная оболочка PID 1156 породил бы ребенка через fork() с PID 1157 затем звоните execve("/bin/df",["df","-h"],&environment), который делает /bin/df -h выполненный с PID 1157. Теперь оболочка только должна ожидать процесса, чтобы выйти и возвратить управление ему.

В случае, если, где необходимо создать канал между двумя или больше командами, сказать df | grep, Вам нужен способ создать два дескрипторов файлов (это читается, и запишите конец канала, которые прибывают из pipe() syscall), затем так или иначе позвольте двум новым процессам наследовать их. Это сделало разветвляющийся новый процесс и затем путем копирования конца записи канала через dup2() звоните на stdout иначе fd 1 (поэтому, если конец записи является fd 4, мы делаем dup2(4,1)). Когда exec() метать икру df происходит дочерний процесс ничто не будет думать о stdout и запишите в него, не будучи знающими (если это активно не проверяет), что его вывод на самом деле идет канал. Тот же процесс происходит с grep, кроме нас fork(), возьмите конец чтения канала с fd 3 и dup(3,0) перед порождением grep с exec(). Все это время родительский процесс все еще там, ожидая для восстановления управления, после того как конвейерно обрабатывают сделанный завершенный.

В случае встроенных команд обычно окружайте, не делает fork(), за исключением source команда. Подоболочки требуют fork().

Короче говоря, это - необходимый и полезный механизм.

Недостатки разветвления и оптимизации

Теперь, это отличается для неинтерактивных оболочек, такой как bash -c '<simple command>'. Несмотря на fork()/exec() быть оптимальным методом, где необходимо обработать много команд, это - трата ресурсов, когда у Вас есть только одна единственная команда. Заключить Stéphane Chazelas в кавычки из этого сообщения:

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

Поэтому много оболочек (не просто bash ) использовать exec() позволить это bash -c '' быть принятым той единственной простой командой. И точно по причинам вышеизложенные, минимизирующие конвейеры в сценариях оболочки лучше. Часто Вы видите, что новички делают что-то вроде этого:

cat /etc/passwd | cut -d ':' -f 6 | grep '/home'

Конечно, это будет fork() 3 процесса. Это - простой пример, но рассмотрите большой файл в диапазоне Гигабайтов. Это было бы намного более эффективно с одним процессом:

awk -F':' '$6~"/home"{print $6}' /etc/passwd

Трата ресурсов на самом деле может быть формой Атаки "отказ в обслуживании", и в особенности fork-бомбы создаются через функции оболочки, которые называют себя в конвейере, который ветвления несколько копий себя. В наше время это смягчено через ограничение максимального количества процессов в cgroups на systemd, который Ubuntu также использует начиная с версии 15.04.

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

См. также

2
ответ дан 23 November 2019 в 00:39

Другие вопросы по тегам:

Похожие вопросы: