Написание программ на PHP с использованием fork()
Параллельные программы на PHP
Реализация
Реализация этого системного вызова в PHP очень проста:
Что такое системный вызов fork()
Системный вызов fork() в *nix-системах представляет из себя такой системный вызов, который делает полную копию текущего процесса. Системный вызов fork() возвращает своё значение два раза: родитель получает PID потомка, а потомок получает 0. Как ни странно, во многих случаях только этого достаточно для того, чтобы писать приложения, использующие несколько CPU.
Подводные камни при использовании fork()
На самом деле fork() делает свою работу не задумываясь о том, что находится у пользовательского процесса в памяти — он копирует всё, например функции, которые зарегистрированы через atexit (register_shutdown_function). Пример:
К сожалению, PHP в конце выполнения скрипта осуществляет вызов деструкторов (в том числе и внутренних деструкторов ресурсов соединений с базой данных). Пример для расширения mysqli:
Вывод программы будет не обязательно таким, как написано. Иногда потомок «успевает» до исполнения процедуры закрытия соединения в родителе и всё работает, как надо.
Боремся с отложенным исполнением функций / деструкторов
На самом деле, проблему с отложенным исполнением можно решить, если вы точно знаете, что хотите. Например, в Си есть функция _exit(), которая выходит, не запуская никаких установленных обработчиков. К сожалению, в PHP такой функции нет, но её поведение можно частично эмулировать с использованием сигналов:
Этого «хака» нам будет достаточно, чтобы соединение с базой оставалось активным для двух PHP-процессов одновременно, хотя лучше, конечно, так на практике не делать :):
Пишем grep
Давайте теперь, для примера, напишем простенькую версию grep, которая будет искать по маске в текущей директории.
Пишем параллельную версию grep
Осталось заменить foreach() на использование нашей функции parallelForeach и добавить обработку ошибок:
Проверим работу нашего грепа на исходном коде PHP 5.3.10:
Работает! Я описал один из часто используемых паттернов при параллельном программировании на PHP — параллельная обработка очереди из задач. Надеюсь, моя статья кому-нибудь поможет перестать бояться писать многопоточные приложения на PHP, если задача допускает такую декомпозицию, как в примере с грепом. Спасибо.
Что такое форк в программировании
Это когда делаешь свою версию какого-то софта
У разработчиков есть понятие «форк» — оно часто встречается в командной разработке и в проектах с открытым кодом. Посмотрите, что это, и когда вам предложат сделать форк, вы будете во всеоружии.
Пример: игра в дурака
Игра в дурака — одна из самых простых карточных игр. У всех на старте по 6 карт, есть колода, есть козырные карты, все ходят по очереди и подкидывают друг другу карты, чтобы самому остаться без карт.
Но в разных компаниях принято играть в дурака по-разному:
И ещё десяток разных вариантов в зависимости от компании или от того, как принято в этом месте.
Все основные правила дурака сохраняются, просто каждая версия добавляет в него что-то своё (например, возможность перевести ход). Но в основе это всё тот же дурак. Если вы знаете основные правила, то легко сможете разобраться в каждом новом варианте игры.
Все эти разные версии игры — это форки исходного дурака. Запомните пока эту аналогию, а чтобы было проще разбираться дальше, вот вам ещё одна мысль:
👉 Форк — это новая программа на базе какой-то версии исходной программы. Она может дальше развиваться самостоятельно, без оглядки на исходную программу.
Откуда такое название
Форк в переводе с английского — это вилка, развилка или ответвление (fork). Этот термин стали использовать, потому что ручка вилки — это как будто основная программа, а зубцы — ответвления от неё. Чтобы было понятнее, вот картинка с форками игры в дурака.
Каждое ответвление — это своя отдельная версия игры, которая живёт и развивается по своим правилам. А вместе все эти ответвления напоминают вилку — отсюда и название.
Если есть время, посмотрите ветку форков Линукса в википедии. Предупреждаем: она большая и это надолго.
Форки в программировании
В программировании всё работает точно так же: у некоторых программ появляются самостоятельные ответвления, которые начинают жить своей жизнью. Но для этого нужно два условия:
👉 Чтобы форк был успешным, в него тоже нужно вкладывать много времени и сил, как в основной продукт. Если им не заниматься, форк умрёт. Та же судьба ждёт форк, если им никто не будет пользоваться — в конце концов чаще всего разработчикам надоедает поддерживать софт просто так и они его забрасывают.
Примеры форков
В современном мире многие успешные сервисы, продукты и программы — это форки. Смотрите сами:
А вот ещё интересный случай, как бывает с форками. Сначала была библиотека node.js, потом от неё пошёл форк io.js, а в 2015 году этот форк стал официальным node.js.
Зачем нужны форки
Причин может быть много:
👉 Главная причина появления форков — желание сделать лучше. Но обычно получается не лучше, а просто по-другому.
Форк и версии
Иногда форк путают с версиями, хотя у них есть важное отличие:
Как сделать свой форк
Git fork. Зачем нужны форки и как с ними работать
Как и в случае с ребейзом, с форками в гите я разобрался не сразу. Вроде ничего особенного там нет, это просто копия репозитория, но натыкаешься на какие-то подводные камни и не сразу понимаешь, как их обойти. Да и вообще, зачем нужны эти форки, тоже осознаешь не сразу. Когда врубаешься, все становится просто, ну это как всегда.
Я не сторонник подхода «чо тут не понимать, тупой штоле» и попробую рассказать человеческими словами, что вообще такое форки, зачем они нужны и как с ними работать. А вы оцените, как получилось. Синьор git девелоперам статья покажется банальщиной, но тем, кто еще не успел обрести такой титул, будет полезно.
Ты работаешь в компании Company в какой-то команде и со своими ребятами пишешь код, например, блога вашего сайта. Рядом сидят ребята из другой команды, которые занимаются админкой. У каждой команды отдельный репозиторий, вы работаете и друг другу не мешаете.
Доступа к их репозиторию нет, поэтому идете договариваться. Обсуждаете вместе детали. Так, нужно завести поле в базе, какое-нибудь булево isAllowedComments, соответственно, подготовить миграцию, вывести это поле отдельной колонкой в таблице пользователей, там поставить чекбокс и уметь сохранять это в базу. А еще в апи, которое вы дергаете, нужно добавить в респонсе это самое поле. Вроде немного, ребята говорят, окей, через пару дней сделаем. И на самом деле делают, все хорошо.
Небольшие просьбы вроде этой появляются все чаще. То вам нужно накатить миграцию и разбанить всех пользователей, которые зарегались больше года назад. То в апи в респонсе вам понадобилось количество комментов пользователя. И каждый раз вы идете с такой мелочью к соседям и просите это сделать.
Рано или поздно ребятам из админки это надоедает, потому что у них хватает и своей работы. Они предлагают вам дать права на репозиторий и пробовать такие небольшие задачи делать самим. Говорят, мол, с такой фигней типа добавления поля в респонс вы и сами справитесь. Если что, зовите, поможем. Прав на пуш в мастер, конечно, никто не даст, но свои ветки создавайте и присылайте мердж-реквесты. Мы смотрим, если все хорошо, то мерджим в мастер и выкладываем в прод. Все довольны. И нам работы меньше, и вам не ждать, пока у нас руки дойдут до вашей задачи.
Вы с командой соглашаетесь. Ты клонируешь их репозиторий и пилишь фичи. Примерно так
Дальше идешь в битбакеты-гитхабы, в репозиторий админки, делаешь мердж-реквест и ждешь, когда его примут. Обычная схема. Мердж-реквест примерно везде делается одинаково: ищешь кнопку «Создать мердж-реквест» или пулл-реквест, выбираешь свою ветку, затем выбираешь, куда сливать (обычно уже по умолчанию будет мастер) и жмешь «Отправить».
Проходит пара месяцев. Ты случайно замечаешь в репозитории админки ветку petya/update-email. Спрашиваешь ребят из админки, а что за Петя вам коммитит? Те говорят, а, это чувак из отдела емейлов, мы им тоже доступ дали, как и вам. Они тоже приходили к нам по 3 раза в месяц, мы задолбались и теперь с ними работаем по вашей схеме. Ничего, все довольны.
Проходит год. Команд, которые работают по такой схеме, уже десяток. В репозитории появляются ветки
Эти ветки множатся, мерджатся между собой и вообще живут своей жизнью. Называются они как попало, коммиты подписывают кто во что горазд, старые ветки не удаляются после мерджей. Надвигается хаос.
Конечно, ребятам из админки не очень нравится такой мусор в их репозитории. Сначала они пытаются с этим бороться и проводить разъяснительные работы. Пытаются рассказать о правилах приличия в их репозитории. Пишут доку и официальное письмо всем программистам, где все подробно рассказывают. Как именовать ветки, как подписывать коммиты, не забывать удалять старые ветки, не заводить лишние ветки без надобности и прочее.
Это дело хорошее, но бесполезное. В компании полсотни разрабов, у каждого своя команда и свои правила работы с репозиторием. Люди приходят и уходят, эти правила уже никто не помнит, да и всем пофигу, главное фичу сделать, чего там заморачиваться с ветками и коммитами. Пусть у команды админки об этом голова болит. Главное, у нас все хорошо, в своем командном проекте, потому что мы никого к себе не пускаем, ахаха. Поэтому и ветки красиво называются, и коммиты адекватно подписаны. Нас тут 5 человек работают, уж между собой-то договоримся.
А между тем в репозитории админки образуется все больший трэш. То злодеи опять пушат ветки с названиями не по ГОСТу, и тимлида бесят. То сам тимлид с похмелья выдал новому чуваку доступы на пуш в мастер, а тот на радостях запушил миграцию, которая пол-сайта уронила. То по ошибке смерджили petya/hotfix вместо vasya/hotfix. В общем весело всем, кроме ребят из админки.
И вот однажды что-то изменилось. Тебе понадобилось запилить новую фичу и ты привычно набиваешь
Сначала идешь в гитхаб-битбакет и ищешь там в нужном репозитории кнопку Fork. Обычно она недалеко от кнопки clone. Жмешь на fork и для тебя создается проект. Если исходный был company/adminka.git, то твой будет примерно vasya/adminka.git. Ну или примерно так. Дальше клонируешь его как обычно
Естественно, свой форк клонируешь, а не оригинальный. Затем там создаешь привычно ветку и пушишь ее.
Заходишь в гитхаб-битбакет исходного репозитория, жмешь «Создать мердж-реквест», выбираешь свою ветку, она будет называться примерно vasya/adminka/blog/new-field, и ждешь, пока мердж-реквест примут.
Пока все ровно так, как ты привык, ничего нового. Мердж-реквест принимают и все хорошо.
Через месяц тебе нужно сделать еще одну задачу. Ты привычно подтягиваешь мастер и настораживаешься
За месяц нет новых коммитов в мастере? Да не верю. Идешь в репозиторий админки, смотришь, там до фига коммитов, чуть не каждый день в мастер пушат. В чем дело?
Присматриваешься, откуда ты пулишься и видишь, что это vasya/adminka. Это твой репозиторий, твой форк. Конечно, ты его месяц не трогал и он ничего не знает о новых коммитах в исходном проекте. Их нужно подтянуть и только тогда создавать новую ветку. Можно сделать это так
То есть вместо origin указываешь адрес нужного репозитория. И вот теперь-то подтянется мастер именно исходного проекта, а не твоего. А еще лучше сделать так, чтобы не набивать каждый раз длинный адрес
Вот теперь твой мастер актуален, можешь создавать ветку и пушить в репозиторий. Пушить уже в origin, то есть свой, потому что в upstream, исходный, тебе пушить никто не даст. То есть еще раз, пулишь мастер так
а пушишь свою ветку так
По сути это все, что нужно знать о работе с форками. После этого идешь создавать мердж-реквест, твоя ветка уже видна в исходном репозитории. Если ты работал над веткой, а за это время в мастер накидали еще коммитов, то перед отправкой мердж-реквеста стоит отребейзиться от мастера
Тут можешь делать, как хочешь, твой форк, твои правила. Можешь пушить с форсом, как у меня в примере. Если не хочешь с форсом, то можешь создать отдельную ветку и положить коммит в нее. Как угодно. Главное, чтобы твои коммиты лежали поверх свежих коммитов из мастера. Тогда никто не будет возникать при рассмотрении мердж-реквеста, а ты будешь уверен, что у тебя самые свежие обновления.
И на этом все. Главное, что нужно сделать, это добавить upstream и четко понимать, в чем его отличие от origin.
С форками можно работать и в компании на полсотни разработчиков, но особенно это популярно в опенсорсе. Возьмите какой-нибудь проект, например, vuejs, у него больше 20 тысяч форков и 3 сотни контрибьюторов. Представьте, что бы творилось в репозитории, пусти всех этих энтузиастов пушить свои правки. А так они спокойно работают со своими форками и присылают пулл-реквесты, не засоряя проект.
Всем удачи. Любите гит, он совсем не страшный 
Русские Блоги
Fork () многопроцессорного программирования
Каталог статей
1. Обзор процесса
Студенты, которые хотят узнать больше о процессе, могут переехать
Команда ps aux используется для просмотра всех идентификаторов процессов.
Команда Linux для остановки процесса: kill [идентификатор процесса] или killall [имя процесса]
Linux останавливает выполняющуюся функцию процесса: int kill(pid_t pid, int sig);
Функция для получения идентификатора процесса:
2. Обзор пространства памяти процесса.
В этой части Сяохэй написал еще один блог, и незнакомые студенты могут двигаться:
https://blog.csdn.net/weixin_46027505/article/details/105076010
Также есть блоги о зомби-процессах:
3. Система вызывает fork ().
В Linux есть два основных системных вызова, которые можно использовать для создания дочерних процессов: fork () и vfork (). Fork в переводе с английского означает «вилка». Почему это имя? Поскольку процесс запущен, если используется fork, порождается другой процесс, и процесс «разветвляется», поэтому имя очень яркое.
Ниже мы приводим образец кода для дальнейшего понимания fork ()
Мы наблюдаем за результатами операции. И родительский процесс, и дочерний процесс выполняют свои собственные операции над значением исходной глобальной переменной, указывая на то, что a = 10 имеет собственное независимое пространство между родительским и дочерним процессами и не мешает друг другу.
И мы обнаружили, что после fork был напечатан дважды, потому что дочерний процесс завершился и запустился один раз, а родительский процесс завершился и запустился.
4. Системный вызов vfork ()
Перед реализацией копирования при записи (это будет подробно описано позже) разработчики Unix всегда беспокоились о бесполезной трате адресного пространства, вызванной выполнением exec сразу после fork.
Таким образом, старейшины придумали vfork ()
За исключением того, что дочерний процесс должен немедленно выполнить системный вызов exec или вызвать _exit () для выхода, результат успешного вызова vfork () такой же, как и fork ().
5. Разница между форком и vfork
| fork() | vfork() |
|---|---|
| Дочерний процесс копирует сегмент данных и сегмент кода родительского процесса. | Дочерний процесс разделяет сегмент данных с родительским процессом.Когда значение переменной в сегменте общих данных нужно изменить, родительский процесс копируется. |
| Порядок выполнения родительского и дочернего процессов неопределен. | Убедитесь, что дочерний процесс запускается первым, а данные передаются родительскому процессу перед вызовом exec или exit, а запуск родительского процесса может быть запланирован после его вызова exec или exit. |
| Родительский и дочерний процессы независимы друг от друга | Если дочерний процесс полагается на дальнейшие действия родительского процесса перед вызовом этих двух функций, это вызовет взаимоблокировку. |
Почему там vfork, потому что предыдущий форк глуп, когда он создает дочерний процесс, он создаст новый адрес
space, и скопируйте ресурсы родительского процесса, и часто выполняйте вызов exec в дочернем процессе, чтобы предыдущая копия работала
— пустая трата усилий.
В этом случае умные люди придумали vfork, и порожденный им дочерний процесс был временно связан с
Родительский процесс разделяет адресное пространство (фактически, концепцию потока), потому что в это время дочерний процесс находится в адресном пространстве родительского процесса.
запущен, поэтому дочерний процесс не может выполнять операции записи, и когда сын «занимает» дом Лао-цзы, со мной, должно быть, поступили несправедливо.
не работает, пусть отдыхает снаружи (блокирует), как только сын выполняет exec или выходит, он купил свой
Сейчас дом, и на этот раз он расположен относительно разделения.
// Вышеупомянутый абзац виден в Интернете, думаю, это хорошо, Если вы забыли конкретный источник, вы не сможете прикрепить ссылку. Если первоначальный автор видит это, я надеюсь, что вы понимаете и можете оставить сообщение. Я прикреплю ссылку.
6, копирование при записи
Linux использует метод копирования при записи, чтобы уменьшить накладные расходы, вызванные полным копированием процесса пространства родительского процесса при fork.
После fork () exec часто вызывается для выполнения другой программы, и exec оставит текстовый сегмент, сегмент данных и стек родительского процесса и загрузит другую программу, поэтому многие реализации fork () теперь не выполняют родительский Полная копия сегмента данных процесса, кучи и стека.
Его предпосылка проста: если есть несколько процессов, которые хотят читать свою собственную копию ресурсов отдела, то в копировании нет необходимости. Каждому процессу нужно только сохранить указатель на этот ресурс. Пока ни один процесс не хочет изменять свою собственную «копию», существует иллюзия, что каждый процесс, кажется, монополизирует этот ресурс. Это позволяет избежать бремени копирования. Если процесс хочет изменить свою собственную «копию» ресурса, он скопирует этот ресурс и предоставит скопированную копию процессу. Однако копирование прозрачно для процесса. Этот процесс может изменить скопированный ресурс, в то время как другие процессы по-прежнему совместно используют неизмененный ресурс.
Отсюда и название: копировать при записи.
6.1 Преимущества копирования при записи
Основное преимущество копирования при записи заключается в том, что, если процессу не требуется изменять ресурс, его не нужно копировать.
Преимущество ленивых алгоритмов заключается в том, что они максимально откладывают дорогостоящие операции до тех пор, пока они не будут выполнены, когда это необходимо.
6.2 Копирование при записи расширенных знаний
1. В случае использования виртуальной памяти копирование при записи выполняется постранично. Следовательно, пока процесс не изменяет все свое адресное пространство, ему не нужно копировать все адресное пространство. После завершения вызова fork () и родительский, и дочерний процессы считают, что у них есть собственное адресное пространство, но на самом деле они совместно используют исходные страницы родительского процесса, и эти страницы могут использоваться другими родительскими или дочерними процессами.
2. Реализация копирования при записи в ядре очень проста. Структуры данных, относящиеся к страницам ядра, могут быть помечены как доступные только для чтения и копирования при записи. Если процесс пытается изменить страницу, он генерирует прерывание из-за ошибки страницы. То, как ядро обрабатывает прерывание из-за ошибки страницы, заключается в создании прозрачной копии страницы. В это время атрибут COW страницы будет очищен, что означает, что к ней больше не предоставляется общий доступ.
3. В современной архитектуре компьютерной системы поддержка копирования при записи на аппаратном уровне обеспечивается блоком управления памятью (MMU), поэтому реализация очень проста. При вызове fork () копирование при записи является большим преимуществом. Поскольку за большим количеством вилок будет следовать exec, то копирование содержимого всего адресного пространства родительского процесса в адресное пространство дочернего процесса является пустой тратой времени: если дочерний процесс немедленно выполняет образ нового двоичного исполняемого файла, его предыдущий Адресное пространство будет заменено. Копирование при записи может оптимизировать эту ситуацию.
7. Что дочерний процесс наследует от родительского?
8. Другие связанные функции API.
Все перечисленные выше семь различных функций называются функциями exec, и вызов любой из этих функций после fork () заставит вновь созданный процесс выполнить другую программу.
wait () и waitpid ()
Функции wait и waitpid используются для работы с мертвыми процессами.
Сюй Сяохэй до этого написал блог по теме:
https://blog.csdn.net/weixin_46027505/article/details/105097361
system () (используйте с осторожностью) и popen ()
Если мы хотим выполнить другую команду Linux в программе, мы можем вызвать fork (), а затем exec для выполнения соответствующей команды, но это относительно проблематично. В системе Linux предусмотрена функция библиотеки system (), которая может быстро создать процесс для выполнения соответствующих команд.
Студенты этих двух функций могут также искать связанные блоги, чтобы узнать
Русские Блоги
Подробное объяснение форка, вфорка и клона в Linux (разница и связь)
Поделитесь учебником по искусственному интеллекту моего учителя! Нулевой фундамент, легко понять!http://blog.csdn.net/jiangjunshow
Вы также можете перепечатать эту статью. Делитесь знаниями, приносите пользу людям и осознайте великое омоложение нашей китайской нации!
fork,vfork,clone
Стандартный системный вызов процесса копирования Unix (то есть, fork), но Linux, BSD и другие операционные системы не только реализуют эту. В частности, Linux реализует три, fork, vfork, clone (специально созданный vfork Это легкий процесс, также называемый потоком, процесс, который разделяет ресурсы)
| Системный вызов | описание |
|---|---|
| fork | Дочерний процесс, созданный с помощью fork, является полной копией родительского процесса, копируя ресурсы родительского процесса, в том числе содержимое памяти. |
| vfork | Дочерний процесс, созданный vfork, делит сегмент данных с родительским процессом, а дочерний процесс, созданный vfork (), будет выполняться перед родительским процессом. |
| clone | При создании потоков в Linux обычно используется библиотека pthread. Фактически, Linux также предоставляет нам системный вызов для создания потоков, который является клоном. |
Из результатов выполнения видно, что процессы отца и сына имеют разные pids, а ресурсы стека и данных полностью копируются.
Дочерний процесс изменил значение count, в то время как количество в родительском процессе не изменилось.
Адрес (виртуальный адрес) дочернего процесса и количество родительских процессов совпадают (обратите внимание, что они сопоставлены с разными физическими адресами в ядре)
При написании копии
Некоторые люди думают, что такое большое количество копий приведет к слишком низкой эффективности выполнения. Фактически, в процессе репликации Linux использует стратегию копирования при записи.
Дочерний процесс копирует task_struct родительского процесса, пространство системного стека и таблицу страниц, что означает, что перед выполнением программы count ++ дочерний процесс и счетчик родительского процесса находились в одной и той же памяти. Когда дочерний процесс изменяет переменную (то есть, переменная записывается), для соответствующей страницы будет создана новая копия с помощью copy_on_write.
Поэтому, когда мы выполняем ++ count, тогда дочерний процесс создает новую страницу для копирования содержимого исходной страницы. Копирование основных ресурсов является необходимым и эффективным. Все выглядит так, как будто независимое пространство хранения родительского процесса также копируется снова.
Копировать (Copy-на-записи) записываемогоЭто стратегия оптимизации, используемая в области программирования. Основная идея заключается в том, что если несколько абонентов запрашивают один и тот же ресурс одновременно, они совместно получат один и тот же индикатор, указывающий на один и тот же ресурс, и система не будет в действительности реплицироваться, пока вызывающая сторона не попытается изменить ресурс. Личная копия предоставляется вызывающей стороне для предотвращения непосредственного обнаружения измененного ресурса, и этот процесс прозрачен только для других вызовов. Основным преимуществом этого подхода является то, что если вызывающая сторона не изменяет ресурс, личная копия не создается.
В первом поколении систем Unix было реализовано создание процесса в стиле дурака: когда был выполнен системный вызов fork (), ядро скопировало все адресное пространство родительского процесса, как было, и выделило скопированную копию дочернему процессу. Такое поведение занимает много времени, потому что оно требует:
Выделите фреймы страниц для таблицы страниц дочернего процесса
Выделите фреймы страниц для страниц дочерних процессов
Инициализировать таблицу страниц дочернего процесса
Скопируйте страницу родительского процесса на соответствующую страницу дочернего процесса
Этот метод создания адресного пространства включает много обращений к памяти, использует много циклов ЦП и полностью уничтожает содержимое кэша. В большинстве случаев это часто не имеет смысла, поскольку многие дочерние процессы начинают свое выполнение с загрузки новой программы, которая полностью отбрасывает унаследованное адресное пространство.
Текущее ядро Linux использует более эффективный метод, называемый Copy On Write (COW). Идея довольно проста: родительский и дочерний процессы совместно используют фрейм страницы вместо копирования фрейма страницы. Тем не менее, пока рамки страницы являются общими, они не могут быть изменены, то есть рамки страницы защищены. Независимо от того, когда родительский процесс или дочерний процесс пытается записать общий фрейм страницы, возникает исключение, тогда ядро копирует страницу в новый фрейм страницы и помечает ее как доступную для записи. Исходный фрейм страницы по-прежнему защищен от записи: когда другие процессы пытаются писать, ядро проверяет, является ли процесс записи единственным владельцем фрейма страницы, и, если это так, помечает фрейм страницы как доступный для записи.
Когда процесс A использует системный вызов fork для создания дочернего процесса B, поскольку дочерний процесс B фактически является копией родительского процесса A,
Следовательно, он будет иметь те же физические страницы, что и родительский процесс. Чтобы сэкономить память и ускорить скорость создания, функция fork () позволит дочернему процессу B совместно использовать физические страницы родительского процесса A только для чтения. В то же время родительский процесс A Права доступа к странице также установлены только для чтения.
Таким образом, когда родительский процесс A или дочерний процесс B выполняют операцию записи на этих общих физических страницах, будет сгенерировано прерывание исключения из-за сбоя страницы (page_fault int14). В это время ЦП выполнит функцию обработки исключений do_wp_page (), предоставленную системой. Разрешите это исключение.
do_wp_page () отменит совместное использование физической страницы, вызвавшей ненормальное прерывание записи, скопирует новую физическую страницу для процесса записи, чтобы у родительского процесса A и дочернего процесса B была физическая страница с одинаковым содержимым. Наконец, из исключения Когда функция обработки вернется, ЦПУ повторно выполнит инструкцию операции записи, которая только что вызвала исключение, так что процесс продолжит выполняться.
vfork
Если простой метод vfork () для fork более популярен, то структура виртуального адресного пространства дочернего процесса не создается ядром, а виртуальное пространство родительского процесса используется совместно. Конечно, этот подход разделяет физический родительский процесс. пространство
Дочерний процесс, созданный vfork, также приведет к зависанию родительского процесса, если только дочерний процесс exit или execve не вызовет родительский процесс.
Дочерний процесс, созданный vfok, совместно использует всю память родительского процесса, включая адрес стека, пока дочерний процесс не использует execve для запуска нового приложения.
Дочерний процесс, созданный vfork, не должен использовать return для возврата к вызывающей стороне или использовать exit () для выхода, но он может использовать функцию _exit () для выхода
Если мы используем return для выхода, вы обнаружите, что программа поймана в хаотически повторяющееся состояние vfork
Мы обнаружим, что дочерний процесс vfork возвращается в место вызова после использования return, поэтому родительский процесс создает новый процесс vfork,
Решением этой проблемы является не использование возврата в процессе, а использование вместо этого выхода или _exit
вилка и вфорк
Отличия и связи
Использование vfork () аналогично fork (). Но есть и различия. Конкретные различия сводятся к следующим 3 пунктам
fork () Дочерний процесс копирует сегмент данных и сегмент кода родительского процесса.
Дочерний процесс vfork () разделяет сегмент данных с родительским процессом.
fork () Порядок выполнения родительского и дочернего процессов не определен.
vfork (): убедитесь, что дочерний процесс запускается первым,
vfork () гарантирует, что дочерний процесс запускается первым, а родительский процесс может быть запланирован для запуска только после того, как он вызовет exec или _exit. Если в
Перед вызовом этих двух функций дочерний процесс зависит от дальнейших действий родительского процесса, которые вызовут взаимоблокировку.
Перед вызовом exec или _exit данные с родительским процессом являются общими, и родительский процесс может быть запланирован для запуска после того, как он вызовет exec или _exit. Если дочерний процесс зависит от дальнейших действий родительского процесса перед вызовом этих двух функций, это вызовет взаимоблокировку. Когда значение переменной в сегменте общих данных необходимо изменить, родительский процесс копируется
vfork используется для создания нового процесса, и целью нового процесса является выполнение нового процесса. vfork и fork создают дочерний процесс, но он не полностью копирует адресное пространство родительского процесса в дочерний процесс и не будет копировать Таблица страниц. Поскольку дочерний процесс немедленно вызовет exec, адресное пространство не будет сохранено. Но перед вызовом exec или exit в дочернем процессе он запускается в пространстве родительского процесса.
Если дочерний процесс зависит от дальнейших действий родительского процесса при вызове vfork, это вызовет тупик. Это показывает, что этот системный вызов используется для запуска нового приложения. Во-вторых, дочерний процесс запускается непосредственно в пространстве стека родительского процесса после возврата vfork () и использует память и данные родительского процесса. Это означает, что дочерний процесс может уничтожить структуру данных или стек родительского процесса, что приведет к сбою.
Чтобы избежать этих проблем, необходимо убедиться, что после вызова vfork () дочерний процесс не возвращается из текущего фрейма стека, и если дочерний процесс изменяет структуру данных родительского процесса, функция выхода не может быть вызвана.
Дочерний процесс также должен избегать изменения какой-либо информации в глобальной структуре данных или глобальных переменных, поскольку эти изменения могут помешать продолжению родительского процесса. В общем, если приложение не вызывает exec () сразу после fork (), необходимо выполнить тщательную проверку перед заменой fork () на vfork ().
Почему есть вфорк
Поскольку предыдущий ответвление при создании дочернего процесса создает новое адресное пространство, копирует ресурсы родительского процесса и часто выполняет вызовы exec в дочернем процессе, так что предыдущая работа по копированию будет напрасной, В этом случае умный человек придумал vfork. Дочерний процесс, который он породил, начал временно делить адресное пространство с родительским процессом (фактически это понятие потока), потому что в это время дочерний процесс выполняется в адресном пространстве родительского процесса. Таким образом, дочерний процесс не может писать,
Следовательно, vfork предназначен для выполнения системного вызова execve для загрузки новой программы сразу после создания дочернего процесса. Прежде чем дочерний процесс завершает работу или запускает новую программу, ядро гарантирует, что родительский процесс заблокирован
После создания дочернего процесса с помощью функции vfork, дочерний процесс часто вызывает функцию exec для выполнения другой программы. Когда процесс вызывает функцию exec, процесс полностью заменяется новой программой, и новая программа запускается из своей основной функции. Выполнение, поскольку вызов exec не создает новый процесс, поэтому идентификатор процесса до и после не изменяется, exec просто заменяет сегмент текста, данных, кучи и стека текущего процесса другой новой программой.
clone
Функция клона является мощной и принимает много параметров, поэтому созданный им процесс более сложен, чем два предыдущих метода.
Для начала необходимо поговорить о структуре этой функции.
···c
int clone(int (fn)(void ), void *child_stack, int flags, void *arg);
···
| знак | смысл |
|---|---|
| CLONE_PARENT | Родительский процесс созданного дочернего процесса является родительским процессом вызывающего, а новый процесс и процесс, который его создал, становятся «братьями» вместо «отец и сын» |
| CLONE_FS | Дочерний процесс и родительский процесс совместно используют одну файловую систему, включая root, текущий каталог, umask |
| CLONE_FILES | Дочерний процесс и родительский процесс используют одну и ту же таблицу дескрипторов файлов (дескриптор файлов) |
| CLONE_NEWNS | Запустите дочерний процесс в новом пространстве имен, пространство имен описывает иерархию файлов процесса |
| CLONE_SIGHAND | Дочерний процесс и родительский процесс используют одну и ту же таблицу обработчиков сигналов (обработчик сигналов) |
| CLONE_PTRACE | Если родительский процесс отслеживается, дочерний процесс также отслеживается |
| CLONE_VFORK | Родительский процесс приостанавливается до тех пор, пока дочерний процесс не освободит ресурсы виртуальной памяти. |
| CLONE_VM | Дочерний процесс и родительский процесс выполняются в одном и том же пространстве памяти |
| CLONE_PID | PID дочернего процесса совпадает с родительским процессом при его создании |
| CLONE_THREAD | Добавлен в Linux 2.4 для поддержки стандарта потока POSIX, дочерний процесс и родительский процесс совместно используют одну группу потоков |
В следующем примере создается поток (дочерний процесс совместно использует пространство виртуальной памяти родительского процесса, и его нельзя назвать процессом без его собственного независимого пространства виртуальной памяти). Родительский процесс приостанавливается, а дочерний поток освобождает ресурсы виртуальной памяти перед продолжением выполнения.
клон, вилка, vfork разница и связь
Способ реализации
Процедуры обслуживания системных вызовов sys_clone, sys_fork и sys_vfork завершаются вызовом функции do_fork.
Параметры do_fork аналогичны параметрам системного вызова clone, но есть еще один reg (регистр пользовательского режима, сохраняемый стеком ядра). Фактически, другие параметры также принимаются regs
Конкретные параметры разные
fork, vfork:
Сервисная подпрограмма просто вызывает do_fork, но параметры слегка изменены
clone_flags:
sys_fork: SIGCHLD, 0, 0, NULL, NULL, 0
sys_vfork: CLONE_VFORK | CLONE_VM | SIGCHLD, 0, 0, NULL, NULL, 0
Пользовательский стек: все стеки родительского процесса.
parent_tidptr, child_ctidptr имеют значение NULL.



