В Unix всякий раз, когда мы хотим создать новый процесс, мы разветвляем текущий процесс, создавая новый дочерний процесс, который в точности совпадает с родительским процессом; затем мы выполняем системный вызов exec, чтобы заменить все данные из родительского процесса данными для нового процесса.
Почему мы сначала создаем копию родительского процесса, а не создаем новый процесс напрямую?
process
architecture
fork
Sarthak
источник
источник
Ответы:
Короткий ответ -
fork
в Unix, потому что в то время его было легко вписать в существующую систему, и потому что в предшествующей системе в Беркли использовалась концепция вилок.Из «Эволюции системы разделения времени Unix» (соответствующий текст выделен ):
С тех пор Unix развивался.
fork
с последующимexec
больше не единственный способ запустить программу.vfork был создан, чтобы быть более эффективным форком для случая, когда новый процесс намеревается выполнить exec сразу после форка. После выполнения vfork родительский и дочерний процессы совместно используют одно и то же пространство данных, а родительский процесс приостанавливается до тех пор, пока дочерний процесс не выполнит программу или не завершится.
posix_spawn создает новый процесс и выполняет файл за один системный вызов. Он принимает кучу параметров, которые позволяют вам выборочно делиться открытыми файлами вызывающей стороны и копировать расположение сигнала и другие атрибуты в новый процесс.
источник
posix_spawn()
для выполнения тех же заданий повторной репликации, которые можно легко выполнить с помощьюfork()
встроенного кода, являются убедительным аргументом в пользу того,fork()
что их гораздо проще использовать.[Я повторю часть моего ответа отсюда .]
Почему бы просто не иметь команду, которая создает новый процесс с нуля? Разве не абсурдно и неэффективно копировать тот, который будет заменен сразу?
На самом деле это, вероятно, будет не так эффективно по нескольким причинам:
«Копия» производится
fork()
немного абстракции, так как ядро использует копирования при записи системы ; все, что действительно нужно создать, это карта виртуальной памяти. Если копия немедленно вызываетexec()
, большая часть данных, которые были бы скопированы, если бы она была изменена в результате действия процесса, фактически никогда не должна копироваться / создаваться, потому что процесс не делает ничего, требующего его использования.Различные важные аспекты дочернего процесса (например, его окружение) не должны дублироваться индивидуально или устанавливаться на основе сложного анализа контекста и т. Д. Они просто предполагаются такими же, как и у вызывающего процесса, и это довольно интуитивная система, с которой мы знакомы.
Чтобы объяснить # 1 немного дальше, память, которая «копируется», но к которой впоследствии не осуществляется доступ, никогда не копируется, по крайней мере, в большинстве случаев. Исключением в этом контексте может быть то, что если вы разветвили процесс, а потом родительский процесс завершил работу, прежде чем дочерний процесс заменил себя на
exec()
. Я бы сказал, потому что большая часть родительского объекта может быть кэширована, если имеется достаточно свободной памяти, и я не уверен, в какой степени это будет использовано (что будет зависеть от реализации ОС).Конечно, это не делает использование копии более эффективным, чем использование чистого листа - за исключением того, что «чистый лист» не является буквально ничем и должен включать распределение. Система может иметь общий пустой / новый шаблон процесса, который копируется таким же образом 1, но тогда это ничего не спасет по сравнению с форком копирования при записи. Итак, № 1 просто демонстрирует, что использование «нового» пустого процесса не будет более эффективным.
Пункт № 2 объясняет, почему использование вилки, вероятно, более эффективно. Окружение дочернего объекта наследуется от его родителя, даже если это совершенно другой исполняемый файл. Например, если родительский процесс представляет собой оболочку, а дочерний веб-браузер
$HOME
все еще одинаков для них обоих, но, поскольку каждый из них может впоследствии изменить его, это должны быть две отдельные копии. Тот, что в ребенке, производится оригиналомfork()
.1. Стратегия, которая может не иметь большого буквального смысла, но моя точка зрения заключается в том, что создание процесса подразумевает не только копирование его изображения в память с диска.
источник
fork()
может это очень быстро (как упомянул GL, порядка 27 линий сборки). Если вы хотите «создать процесс с нуля» в другом направлении, это будетfork()
стоить лишь чуть-чуть дороже, чем начинать с пустого процесса (27 строк сборки + стоимость закрытия файловых дескрипторов). Так чтоfork
обрабатывает обе вилки и создает хорошо, в то время какcreate
может обрабатывать только хорошо.fork
фактически копировалась вся память процесса, и это было очень дорого.Я думаю, что причина, по которой Unix имела только
fork
функцию создания новых процессов, является результатом философии UnixОни строят одну функцию, которая делает одну вещь хорошо. Это создает дочерний процесс.
Что делать с новым процессом, решать программисту. Он может использовать одну из
exec*
функций и запустить другую программу, или он не может использовать exec и использовать два экземпляра одной и той же программы, что может быть полезно.Таким образом, вы получаете большую степень свободы, так как вы можете использовать
и, кроме того, вам нужно запомнить
fork
толькоexec*
вызовы функций и функций, которые вам приходилось делать в 1970-х годах.источник
Существует две философии создания процесса: разветвление с наследованием и создание с аргументами. Unix использует fork, очевидно. (Например, OSE и VMS используют метод create.) Unix обладает МНОГИМИ наследуемыми характеристиками, а другие периодически добавляются. По наследству эти новые характеристики могут быть добавлены БЕЗ ИЗМЕНЕНИЯ СУЩЕСТВУЮЩИХ ПРОГРАММ! Используя модель создания с аргументами, добавление новых характеристик будет означать добавление новых аргументов к вызову создания. Модель Unix проще.
Это также дает очень полезную модель ветвления без exec, где процесс может быть разделен на несколько частей. Это было жизненно важно еще тогда, когда не было формы асинхронного ввода-вывода, и полезно, когда используются преимущества нескольких процессоров в системе. (Предварительные темы.) Я делал это много за эти годы, даже недавно. По сути, он позволяет объединять несколько «программ» в одну программу, поэтому абсолютно нет места для искажений, несовпадений версий и т. Д.
Модель fork / exec также дает возможность конкретному дочернему элементу наследовать радикально странную среду, установленную между fork и exec. Такие вещи, как унаследованные файловые дескрипторы, особенно. (Расширение stdio fd.) Модель create не предлагает возможности наследовать все, что не было предусмотрено создателями вызова create.
Некоторые системы также могут поддерживать динамическую компиляцию нативного кода, когда процесс фактически создает свою собственную программу с нативным кодом. Другими словами, ему нужна новая программа, которую он пишет сам на лету, БЕЗ необходимости проходить цикл исходного кода / компилятора / компоновщика и занимать место на диске. (Я полагаю, что существует языковая система Verilog, которая делает это.) Модель fork поддерживает это, модель создания обычно не будет.
источник
Функция fork () не только копирует родительский процесс, она возвращает значение, указывающее, что процесс является родительским или дочерним процессом, на рисунке ниже показано, как можно использовать fork () в качестве отца и сын:
как показано, когда процесс является отцом fork () возвращает идентификатор процесса сына,
PID
иначе он возвращает0
Например, вы можете использовать его, если у вас есть процесс (веб-сервер), который получает запросы, и при каждом запросе он создает
son process
для обработки этого запроса, здесь у отца и его сыновей разные задания.ТАК, не запускать копию процесса не совсем так, как fork ().
источник
fork
если вы предполагаете, что хотитеfork
в первую очередьПеренаправление ввода / вывода наиболее легко реализовать после fork и перед exec. Дочерний, зная, что это дочерний элемент, может закрывать файловые дескрипторы, открывать новые, dup () или dup2 (), чтобы получить их на правильный номер fd и т. Д., И все это без воздействия на родителя. После этого и, возможно, изменения любых желаемых переменных среды (также не влияющих на родительский), он может выполнить новую программу в специализированной среде.
источник
Я думаю, что все здесь знают, как работает fork, но вопрос в том, почему нам нужно создать точную копию родительского элемента с помощью fork? Ответ ==> Возьмем пример сервера (без разветвления), когда клиент-1 обращается к серверу, если в это же время прибыл второй клиент-2 и хочет получить доступ к серверу, но сервер не дает разрешения вновь прибывшему client-2, потому что сервер занят обслуживанием client-1, поэтому client-2 должен ждать. После того, как все службы для client-1 завершены, client-2 теперь может получить доступ к серверу. Теперь рассмотрим, если в то же время клиент-3 прибывает, поэтому клиент-3 должен ждать, пока все службы для клиента-2 не будут завершены. Возьмите сценарий, когда тысячи клиентов должны получить доступ к серверу одновременно ... тогда все клиенты должны подождите (сервер занят !!).
Этого можно избежать, создав (используя fork) точную дубликатную копию (т. Е. Дочернюю) сервера, где каждый дочерний элемент (точная дублирующая копия его родительского, т. Е. Сервера) выделен вновь поступившему клиенту, таким образом, одновременно все клиенты получают доступ к одному и тому же сервер.
источник
fork
вызов, который копирует родительский процесс, состоит в том, что вам не нужно иметь две отдельные программы - но наличие отдельных программ (например,inetd
) может сделать систему более модульной.