Почему fork () работает так, как работает - PullRequest
33 голосов
/ 28 ноября 2011

Итак, я использовал fork() и знаю, что он делает. Как новичок я очень боялся этого (и до сих пор не понимаю его полностью). Общее описание fork(), которое вы можете найти в Интернете, состоит в том, что он копирует текущий процесс и назначает другой PID, родительский PID, и процесс будет иметь другое адресное пространство. Все хорошо, однако, учитывая это описание функциональности, начинающий задастся вопросом: «Почему эта функция так важна ... зачем мне копировать мой процесс?». Поэтому я удивился, и в конце концов я обнаружил, что именно так вы можете вызывать другие процессы внутри вашего текущего процесса с помощью семейства execve().

Чего я до сих пор не понимаю, так почему вы должны делать это таким образом? Самым логичным было бы иметь функцию, которую вы можете вызвать как

create_process("executable_path+name",params..., more params); 

, который будет порождать новый процесс и запускать его в начале функции main () и возвращать новый PID.

Что беспокоит меня, так это ощущение, что решение fork / execve выполняет потенциально ненужную работу. Что если мой процесс использует тонны памяти? Копирует ли ядро ​​мои таблицы страниц и тому подобное. Я уверен, что это действительно не выделяет реальную память, если я не коснулся этого Кроме того, что произойдет, если у меня есть темы? Мне просто кажется, что это слишком грязно.

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

Этот поток говорит о той же проблеме, но я нашел ее не вполне удовлетворительной:

Спасибо.

Ответы [ 14 ]

0 голосов
/ 08 июля 2018

Исторически Unix работал на довольно небольших системах, не позволяя запускать более одного процесса в ОЗУ (все они работали в одном и том же адресном пространстве, никакого MMU там не было). fork просто выгружал текущий процесс на диск (или другое вторичное хранилище), не пытаясь поменять местами другой процесс. Вы можете продолжить выполнение копии в памяти или использовать exec для загрузки и продолжить работу с другим исполняемым файлом.

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

0 голосов
/ 14 января 2015

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

Если вы хотите выполнять параллельные вычисления, ваши процессы могут быть разделены на несколько клонов прямо над циклом.Каждый из клонов выполняет подмножество вычислений, пока родитель ожидает их завершения.Операционные системы гарантируют, что они могут работать параллельно.В Windows вам, например, нужно использовать OpenMP, чтобы получить такую ​​же выразительность.

Если вам нужно читать или записывать из файла, но вы не можете ждать, вы можете просто разветвиться, и ваш клон сделает ввод / вывод, пока выпродолжить исходное заданиеВ Windows вы можете подумать о порождении потоков или использовании перекрывающегося ввода-вывода во многих ситуациях, когда в Unix подойдет простой форк.В частности, процессы не имеют таких проблем с масштабируемостью, как потоки.Это особенно верно для 32-битных систем.Просто разветвление гораздо удобнее, чем разбираться со сложностями перекрывающегося ввода-вывода.Хотя процессы имеют свое собственное пространство памяти, потоки живут в одном и том же, и, таким образом, существует ограничение на количество потоков, которые вы должны рассмотреть для добавления в 32-битный процесс.Создание 32-битного серверного приложения с помощью fork очень просто, а создание 32-битного серверного приложения с потоками может быть кошмаром.И поэтому, если вы программируете на 32-битной Windows, вам придется прибегнуть к другим решениям, таким как перекрывающийся ввод-вывод, с которым PITA работает.

Поскольку процессы не разделяют глобальные ресурсы, такие как потоки (например, глобальная блокировка в malloc) это гораздо более масштабируемо.Хотя потоки часто блокируют друг друга, процессы работают независимо.

В Unix, поскольку fork создает клон процесса копирования при записи, он не тяжелее, чем порождает новый поток в Windows.

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

Другое дело, что здесь есть проблема безопасности.Процессы не разделяют пространство памяти и не могут испортить внутренние детали друг друга.Это приводит к повышению стабильности.Если у вас есть сервер, который использует потоки, сбой в одном потоке уничтожит все приложение сервера.С разветвлением крушения снесет только раздвоенный клон.Это также упрощает обработку ошибок.Часто бывает достаточно, чтобы ваш разветвленный клон прервался, поскольку это не имеет значения для исходного приложения.

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

0 голосов
/ 28 ноября 2011

Основной причиной использования форка является скорость выполнения.

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

Также в большинстве случаев программа будет ".so".или ".dll", чтобы исполняемые инструкции не копировались только в стек и в кучу.

0 голосов
/ 28 ноября 2011

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

...