Аналог fork в windows

Обновлено: 02.07.2024

Решение

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

Что касается потоков, fork создает только один новый поток в дочернем процессе, который напоминает копию вызывающего потока.

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

Если ваш родительский процесс имеет много открытых файловых дескрипторов, то я предлагаю вам пройти /proc/self/fd и закройте все файловые дескрипторы в дочернем элементе, которые вам не нужны.

Другие решения

Копии «копируются при записи», поэтому, если ваш дочерний процесс не изменяет данные, он не будет использовать никакую память, кроме памяти родительского процесса. Как правило, после fork() Дочерний процесс делает exec() чтобы заменить программу этого процесса на другую, тогда вся память сбрасывается в любом случае.

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

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

Я не использовал CreateProcess , но fork() не является точной копией процесса. Он создает дочерний процесс, но дочерний процесс начинает выполнение с так же инструкция, в которой родитель назвал fork и продолжает оттуда.

Я рекомендую взглянуть на Глава 5 из Три легкие фигуры ОС книга. Это может помочь вам начать поиск нужного вам ребенка.


Смотри исходники Chromium'а. ЕМНИП в венде есть аналогичный механизм.

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


Пока именно этим и занимаюсь.


Ещё один компелятор. Нет.


почему бы не использовать WinApi для создания потоков.

А где про них можно прочитать?



Ещё один компелятор. Нет.


Прямого аналога fork() на Windows нет

А в перле как сделано?


А в перле свой fork, отдельный от системного.


Ну да, но ведёт он себя аналогично системному. Это я к тому, что навелосипедить можно, если очень хочется.


навелосипедить можно, если очень хочется

Так в cygwin, ЕМНИП, есть как раз такой велосипедный fork.

Через виндовые потоки.


Потоки таки не подходят. Они рушат всё приложение при крахе.



Можно ли нормально скомпилировать программу МинГВином, но с цугвиновскими библиотеками?

У меня не получилось - даже если подменить хидеры мингвина цугвиновскими и прилинковать libcygwin.a, сыпятся многочисленные варнинги на редефенишионсы.


я бы выкинул минГВ %)


Совсем аналогичного нет :) CreateProcess() имеет гораздо более высокую цену, чем fork(). В венде единицей диспетчеризации является поток.


В венде единицей диспетчеризации является поток.

Не знаю, к чему это, но в линуксе единица диспетчеризации тоже поток.



CreateProcess() имеет гораздо более высокую цену, чем fork(). В венде единицей диспетчеризации является поток.

И причем тут вообще единица диспетчеризации?


И это не мешает планировщику выполнять потоки как отдельные сущности.

Как это связано с диспетчеризацией?

не поток, а task.
Процесс это task, и тред это task.

В винде процесс это просто контейнер, и единица выполнения там тред.


не поток, а task.
Процесс это task, и тред это task.

okay.jpg. Но это больше вопрос терминологии, не?

нет, отличие фундаментально.



Я хочу раскошелиться на windows. Какова наиболее похожая операция и как ее использовать.

Cygwin имеет полнофункциональную вилку () в Windows. Таким образом, если использование Cygwin приемлемо для вас, то проблема решена в случае, если производительность не является проблемой.

в противном случае вы можете посмотреть, как Cygwin реализует fork(). Из довольно старой архитектуры Cygwin doc:

5.6. создание процессов Особенно интересен вызов fork в Cygwin потому что он не отображает хорошо поверх API-интерфейса Win32. Эта марка это очень сложно реализовать правильно. В настоящее время вилка Cygwin является копирования при записи подобно тому, что присутствовало в начале вкусы UNIX.

первое, что происходит, когда родительский процесс порождает дочерний процесс является ли родитель инициализирует пробел в таблице процессов Cygwin для ребенок. Затем он создает приостановленный дочерний процесс с помощью Win32 Вызова CreateProcess. Далее, родитель процесс вызывает setjmp для сохранения собственного контекст и устанавливает указатель на это в A Cygwin общая область памяти (общая среди всех задач Cygwin). Затем он заполняется в детской .данные и. разделы bss путем копирования из собственного адресного пространства в адрес отстраненного ребенка пространство. После адресного пространства ребенка инициализируется, ребенок запущено родитель ждет мьютекса. Маленький обнаруживает, что он был раздвоен и longjumps с помощью сохраненного буфера перехода. Затем ребенок устанавливает мьютекс родитель ждет и блоки еще один мьютекс. Это сигнал для родитель для копирования стека и кучи в ребенка, после чего он освобождает мьютекс, дочерний ждем и возвращаемся с развилки вызов. Наконец, ребенок просыпается от блокировка на последнем мьютексе, воссоздает любые области, сопоставленные с памятью, переданные ему через общую зону и возвращается из сама вилка.

пока мы имеем некоторые идеи о том, как ускорить внедрение зубца уменьшение количества контекста переключение между родителем и ребенком процесс, вилка будет почти наверняка всегда быть неэффективным под Win32. К счастью, в большинстве случаев семейство вызовов spawn, предоставляемых Cygwin можно заменить на пара fork/exec с небольшим количеством усилие. Эти звонки map чисто на вершине Win32 API. В результате они гораздо эффективнее. Изменение программа драйвера компилятора для вызова spawn вместо fork был тривиальным изменение и расширение компиляции скорости на двадцать-тридцать процентов наш тест.

однако, spawn и exec представляют их собственный набор трудностей. Потому что там нет способа сделать фактический exec под Win32, Cygwin должен изобрести свой собственный Идентификаторы Процессов (PIDs). В результате, когда процесс выполняет несколько exec звонки, будет несколько окон СОИД связан с одним с Cygwin ПИД-РЕГУЛЯТОР. В некоторых случаях заглушки каждого из эти процессы Win32 могут задерживаться, ждут своего старпома, что с Cygwin процесс выход.

звучит как много работы, не так ли? И да, это slooooow.

EDIT: документ устарел, пожалуйста, посмотрите это отлично ответ для обновления

Я, конечно, не знаю подробностей об этом, потому что я никогда этого не делал, но собственный NT API имеет возможность развить процесс (подсистема POSIX в Windows нуждается в этой возможности - я не уверен, что подсистема POSIX даже поддерживается больше).

поиск ZwCreateProcess () должен получить более подробную информацию - например это немного информации от Максима Шацких:

самый важный параметр здесь-SectionHandle. Если этот параметр имеет значение NULL, ядро будет разветвлять текущий процесс. В противном случае это параметр должен быть дескриптором объекта раздела SEC_IMAGE, созданного на EXE-файл перед вызовом ZwCreateProcess ().

Икер Арисменди писал:

этот документ довольно старый, 10 лет или так. В то время как мы все еще используем Win32 вызывает эмуляцию fork, метод заметно изменился. Тем более, мы не создаем дочерний процесс в приостановленном состоянии больше, если конкретные datastructes нужна специальная обработка в родитель, прежде чем они будут скопированы в ребенка. В текущем 1.5.25 отпустите единственный случай для приостановленного ребенка-открытые сокеты в родитель. Предстоящий выпуск 1.7.0 не будет приостановлен вообще.

одной из причин не использовать ZwCreateProcess было то, что до 1.5.25 выпуск мы по-прежнему поддерживаем пользователей Windows 9x. Однако два не удалось использовать ZwCreateProcess в системах на базе NT или иной причине.

было бы очень хорошо, если бы этот материал был лучше или вообще документировано, особенно несколько datastructures и как подключить процесс в подсистему. Хотя fork не является концепцией Win32, я не поймите, что было бы плохо сделать вилку проще в реализации.

Ну, windows на самом деле не имеет ничего подобного. Тем более, что fork можно использовать для концептуального создания потока или процесса в *nix.

Итак, я должен сказать:

CreateThread() (Я слышал, что для приложений C, _beginthreadex() лучше).

люди пытались реализовать fork в Windows. Это самое близкое к нему, что я могу найти:

среди прочего, это указывает на то, что модель процесса сильно отличается между двумя системами и рекомендует учитывать CreateProcess и CreateThread, где требуется fork()-подобное поведение.

до того, как Microsoft представила свою новую опцию "подсистема Linux для Windows", CreateProcess() было самое близкое, что Windows должна fork() , но Windows требует указать исполняемый файл для запуска в этом процессе.

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

возвращаясь в другую сторону, эквивалент Windows CreateProcess() - Это fork()/exec() пара функций в UNIX.

если вы портировали программное обеспечение в Windows, и вы не возражаете против уровня перевода, Cygwin предоставил возможность, которую вы хотите, но это было довольно kludgey.

конечно, С новый подсистема Linux, ближайшая вещь Windows должна fork() is на самом деле fork() : -)

семантика fork() необходима, когда ребенку нужен доступ к фактическому состоянию памяти родителя на момент вызова fork (). У меня есть часть программного обеспечения, которая полагается на неявный мьютекс копирования памяти с момента вызова instant fork (), что делает потоки невозможными для использования. (Это эмулируется на современных платформах * nix через семантику копировать-на-запись/обновление-память-таблица.)

ближайший, который существует в Windows как syscall, - CreateProcess. Лучшее, что может быть сделано для родителя, чтобы заморозить все другие потоки во время копирования памяти в пространство памяти нового процесса, а затем разморозить их. Ни класс Cygwin frok [sic], ни код Scilab, который опубликовал Эрик де Куртис, не замораживают нити, это я вижу.

кроме того, вы, вероятно, не должны использовать функции Zw*, если вы не находитесь в режиме ядра, вы, вероятно, должны использовать функции Nt* вместо этого. Есть дополнительная ветвь, которая проверяет, находитесь ли вы в режиме ядра и, если нет, выполняет все проверки границ и проверки параметров, которые всегда делает Nt*. Таким образом, это очень немного менее эффективно, чтобы вызвать их из пользовательского режима.

"Как только вы хотите сделать доступ к файлу или printf, тогда io отклоняется"

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

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

" кроме того, вы, вероятно, не должны использовать функции Zw*, если вы не находитесь в режиме ядра, вы должны возможно, вместо этого используйте функции Nt*."

Перевод статьи «Top 10 Best GUI Git Client for Developers».

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

Несмотря на то, что многие пользователи вполне комфортно себя чувствуют, пользуясь Git при помощи командной строки, для этого инструмента есть достаточное количество графических интерфейсов. Эти GUI-клиенты способны существенно ускорить вашу работу с системой контроля версий, особенно, если вы еще не слишком хорошо с ней знакомы.

Если вы ищете идеальный графический интерфейс для управления своим репозиторием на Mac, Windows или Linux, вам повезло: сейчас мы вам расскажем о лучших GUI-клиентах Git.

1. GitHub Desktop


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

Функционал приложения позволяет легко замечать пул-реквесты в ветках, а также просматривать различия в версиях изображений и блоков кода. При этом элементы для дальнейшего управления можно добавлять в приложение даже путем перетаскивания.

Приложение доступно для macOS и Windows.

2. Fork


Fork это весьма продвинутый GUI-клиент для macOS и Windows (с бесплатным пробным периодом). В фокусе этого инструмента скорость, дружественность к пользователю и эффективность. К особенностям Fork можно отнести красивый вид, кнопки быстрого доступа, встроенную систему разрешения конфликтов слияния, менеджер репозитория, уведомления GitHub.

С помощью этого инструмента вам будут доступны интуитивный rebase в красивом UI, GIT LFS, cherry-pick, подмодули и многое другое.

3. Tower


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

Есть бесплатный пробный период.

4. Sourcetree


Sourcetree это бесплатный GUI Git для macOS и Windows. Его применение упрощает работу с контролем версий и позволяет сфокусироваться на действительно важных задачах.

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

Sourcetree был разработан Atlassian для Bitbucket, но вполне может использоваться в сочетании с другими Git-платформами. Имеет встроенную поддержку Mercurial-репозиториев.

5. SmartGit


SmartGit это Git-клиент для Mac, Linux и Windows. Имеет богатый функционал. Поддерживает пул-реквесты для SVN, GitHub и Bitbucket. В арсенале SmartGit вы найдете CLI для Git, графическое отображение слияний и истории коммитов, SSH-клиент, Git-Flow, программу для разрешения конфликтов слияния.

SmartGit может использоваться бесплатно в некоммерческих проектах.

6. Sublime Merge


Sublime Merge это клиент Git для Mac, Windows и Linux. Это детище тех же разработчиков, которые создали редактор кода Sublime Text.

Sublime Merge отличается высокой производительностью, а также отличным функционалом для слияния, поиска, просмотра различий между версиями. Базовой версией можно пользоваться бесплатно.

7. GitKraken


GitKraken это кроссплатформенный GUI Git для использования с различными платформами контроля версий (включая GitHub, Bitbucket, GitLab). Его цель — повысить вашу продуктивность в использовании Git. Для этого вам предоставляется интуитивный UI, возможность отслеживать задачи, встроенный редактор кода, редактор конфликтов слияния и поддержка интеграции с другими платформами.

Коммерческое использование платное. Также придется купить Pro-версию, если хотите получить расширенный функционал.

Доступен для Mac, Windows и Linux.

8. GitUp


GitUp это бесплатный (open-source) клиент Git для пользователей Mac. В этом инструменте упор сделан на скорость, простоту, эффективность и легкость использования. Он позволяет вам напрямую взаимодействовать с базой данных хранилища, в результате чего работает куда быстрее аналогов.

В GitUp вы найдете графическую альтернативу для всех функций Git и интуитивно понятную реализацию команд ввода.

9. Aurees Git Client


Это бесплатное приложение для использования Git на Mac, Windows и Linux. Создано оно для ускорения работы, связанной с контролем версий.

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

10. GitBlade


GitBlade — это красивый клиент Git для платформ Mac, Windows и Linux. В нем вы найдете все функции, необходимые в ежедневной работе с Git-проектами (инструменты merge, визуальное отображение связанных веток и коммитов, режим просмотра различий между версиями, визуализация истории файла).

Всеми базовыми функциями можно пользоваться бесплатно (а более продвинутыми — 14 дней пробного периода).

11. Git Cola


Git Cola это совершенно бесплатный клиент Git для Ubuntu и других дистрибутивов Linux, а также Windows и macOS. Один из самых лучших и быстрых инструментов такого типа. Он прост в работе, но отлично справляется со всеми основными задачами, возникающими при работе с версиями.

12. GitEye

GitEye — простой в использовании клиент Git с графическим интерфейсом, созданный CollabNet. Поставляется в комплекте с несколькими инструментами, помогающими повысить продуктивность разработки ПО. Например, Gerrit Code Review выводит уведомления об изменениях или обновлениях в коде.

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

Этот клиент Git хорошо интегрируется с другими популярными инструментами, такими как Bugzilla, Jira, Hudson, Jenkins и пр.

Есть версии для Windows, Linux и Mac.

И напоследок — графический пользовательский интерфейс в виде сайта.

13. UnGit

Этим GUI вы можете пользоваться на любой платформе. После установки UnGit запускает локальный веб-сервер, прослушивающий отдельный порт. Благодаря этому вы получаете интерактивную веб-страницу для вашего репозитория, которую можете открыть в любимом браузере.

UnGit также доступен в виде плагинов для Atom и Sublime Text.

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


Недавно я наткнулся на реализацию popen() (та же идея, другой API) с использованием clone(2), где я открыл issue с запросом использования vfork(2) или posix_spawn() в целях лучшей переносимости на другие платформы. Оказывается, для Linux есть одно очень важное преимущество в использовании clone(2). И вот я думаю, что мне следует раскрыть тему, которую я там затронул, где-нибудь еще: в гисте, в блоге, да где угодно.

Давным-давно я, как и многие фанаты Unix, думал, что fork(2) и модель порождения процессов fork-exec были лучшим решением в мире, а Windows нервно курил в сторонке, имея только exec*() и _spawn*(), последний из которых был чистым виндоусизмом.

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

Такие серьезные заявки требуют объяснений, поэтому позвольте мне объяснить все по порядку.

Я не буду утруждать себя объяснением, что такое fork(2) - если вы это читаете, я полагаю, вы уже знаете. Но я расскажу про vfork(2) и почему он считается опасным. vfork(2) очень похож на fork(2) , но новый процесс, который он создает, запускается в том же адресном пространстве, что и родительский, как если бы он был потоком. Он даже разделяет тот же стек с потоком, который вызвал vfork(2) ! Два потока не могут совместно использовать стек, поэтому родительский процесс останавливается до момента, когда дочерний выполнит свою задачу: либо exec*(2) , либо _exit(2) .

3BSD добавила vfork(2) , а несколько лет спустя 4.4BSD удалила его, так как к тому времени он стал считаться небезопасным. По крайне мере так говорят на страницах нескольких последующих руководств. Но производные 4.4BSD восстановили его и не называют небезопасным. Для этого есть причина: vfork(2) гораздо дешевле, чем fork(2) - намного, намного дешевле. Это потому, что fork(2) должен либо скопировать адресное пространство родителя, либо организовать копирование при записи (что должно быть оптимизацией, чтобы избежать ненужных копий). Но даже копирование при записи очень дорого обходится, потому что требует изменения маппинга памяти, дорогостоящего устранения ошибок страниц и т. д. Современные ядра, как правило, seed’ят дочерний элемент копией резидентного набора родительского объекта, но если родительский элемент имеет большой объем потребляемой памяти (например, JVM), то RSS будет огромным. Таким образом, fork(2) неизбежно дорог, за исключением небольших программ с небольшими объемами потребляемой памяти (например, shell).

Итак, вы начинаете понимать, почему fork(2) - зло. И я еще не дошел до рисков, связанных с fork-safety! Соображения fork-safety во многом схожи с thread-safety (потокобезопасностью), но сделать библиотеки fork-safe сложнее, чем thread-safe. Я не буду здесь вдаваться в тонкости fork-safety: в этом нет необходимости.

(Прежде чем продолжить, я должен признаться в лицемерии: я пишу код, который использует fork(2) , зачастую для многопроцессных демонов, в отличие от многопоточности, хотя я часто использую и последнее. Но форки там происходят очень рано, когда ничего fork-unsafe еще не произошло, а адресное пространство еще мало, что позволяет избежать большинства зол fork(2) . vfork(2) не может использоваться в этих целях. В Windows нужно было бы прибегать к CreateProcess() или _spawn() для реализации многопроцессных демонов, что является большой головной болью.)

Почему я тогда думал, что fork(2) элегантен? По той же причине, что и все остальные: CreateProcess*() , _spawn() и posix_spawn() , и такие функции чрезвычайно сложны, какими они и должны быть, потому что существует огромное количество вещей, которые можно сделать между fork() и exec() , скажем, shell. Но с fork() и exec() не нужен язык или API, которые могут выразить все эти вещи: язык хоста подойдет! fork(2) дал создателям Unix возможность перенести всю эту сложность из kernel-land в user-land, где гораздо проще разрабатывать программное обеспечение - это сделало их более продуктивными, возможно, намного более эффективными. Цена, которую создатели Unix заплатили за эту элегантность, заключалась в необходимости копирования адресных пространств. Поскольку в то время программы и процессы были небольшими, их неэлегантность было легко не заметить. Но теперь процессы имеют тенденцию быть огромными, и это делает копирование даже только резидентного набора родительского объекта и возню с таблицей страниц для всего остального чрезвычайно дорогостоящим занятием.

Но у vfork() есть вся эта элегантность и нет недостатков fork() !

У vfork() есть один недостаток: родительский процесс (в частности, поток в родительском процессе, который вызывает vfork() ) и дочерний процесс совместно используют стек, что требует остановки родительского (потока) до тех пор, пока дочерний процесс не выполнит exec() или _exit() . (Это можно простить из-за того, что vfork(2) давно предшествовал потокам - когда потоки появились, необходимость в отдельном стеке для каждого нового потока стала совершенно очевидной и неизбежной. Исправление для потоковой передачи заключалось в использовании нового стека для нового потока и использовании коллбек-функции и аргумента как main()-подобия для этого нового стека.) Но блокировка - это плохо, потому что синхронное поведение плохо, особенно когда это единственный вариант, но все могло бы быть лучше. Асинхронная версия vfork() должна будет запускать дочерний процесс в новом/альтернативном стеке. Назовем ее afork() или avfork() . afork() должен быть очень похож на pthread_create() : он должен принимать функцию для вызова в новом стеке, а также аргумент для передачи этой функции.

Я должен упомянуть, что все справочные страницы vfork() , которые я видел, говорят, что родительский процесс останавливается до тех пор, пока дочерний не совершит exit/exec, но это предшествует потокам. Linux, например, останавливает только один поток в родительском процессе - тот который вызвал vfork() , а не все потоки. Я считаю, что это правильно, но другие IIRC ОС останавливают все потоки в родительском процессе (что является ошибкой, IMO).

afork() позволил бы API по типу popen() очень быстро возвращать с соответствующими пайпами для I/O с детьми. Если что-то пойдет не так на стороне дочернего процесса, тогда дочерний процесс выйдет, и их выходной пайп (если есть) продемонстрирует EOF, и/или записи на дочерний вход получат EPIPE и/или вызовут SIGPIPE, после чего вызывающий popen() сможет проверить наличие ошибок.

С таким же успехом можно было одолжить флаги forkx()/vforkx() Illumos и сделать так, чтобы afork() выглядел как-то так:

Оказывается, afork() легко реализовать в Linux: это просто вызов clone( <function> , <stack> , CLONE_VM | CLONE_SETTLS , <argument> ). (Вы могли бы хотеть запросит, чтобы SIGCHLD был направлен на родителю, когда ребенок умирает, но это явно не желательно в реализации popen() , так как противном случае программа может извлечь его перед тем, как pclose() сможет извлечь его. Более подробно об этом смотрите на Illumos.)

Можно также реализовать что-то вроде afork () (без Illumos forkx() флагов) в системах POSIX, используя pthread_create() для запуска потока, который будет блокироваться в vfork() , пока вызвавший afork() процесс продолжает свои дела. Добавьте taskq, чтобы заранее создать столько рабочих потоков, сколько необходимо, и у вас будет быстрый afork() . Однако afork() , реализованный таким образом, не сможет вернуть PID, если только потоки не в taskq pre-vfork (хорошая идея!), вместо этого потребуется колбек по завершению, что-то вроде этого:

Если потоки pre-vfork, то может быть реализован возвращающий PID afork() , хотя передача задачи pre-vfork потоку может быть сложной задачей: pthread_cond_wait() может не работать в дочернем процессе, поэтому придется использовать пайп, в который записывается указатель на отправленный запрос. (Пайпы безопасны для использования на дочерней стороне vfork() . То есть, read() и write() на пайпе безопасны в дочернем процессе vfork() ) Вот как это будет работать:

В заголовке также говорится, что clone(2) - это глупо. Позвольте мне объяснить это. clone (2) изначально был добавлен как альтернатива полноценным потокам POSIX, которые можно было использовать для реализации потоков POSIX. Идея заключалась в том, что было бы неплохо иметь множество вариаций fork() , и, как мы видим здесь, это действительно так, что касается avfork() ! Однако avfork() не был изначальной мотивацией. На пути к NPTL было сделано много ошибок.

В Linux должен был быть системный вызов создания потока - тогда он избавил бы себя от боли, связанной с первой реализацией pthread для Linux. Linux следовало извлечь уроки из Solaris/SVR4, где эмуляция сокетов BSD через libsocket поверх STREAMS оказалась очень долгой и дорогостоящей ошибкой. Эмулировать один API из другого API с несоответствием импеданса в лучшем случае сложно.

С тех пор clone() превратился в швейцарский армейский нож - он эволюционировал, чтобы иметь функции входа в зоны/jail’ы, но только своего рода: в Linux нет полноценных зон/jail’ов, вместо этого добавляются новые пространства имен и новые флаги clone(2) , которые идут с ними. И поскольку новые связанные с контейнером флаги clone(2) добавляются, старый код может захотеть их использовать. только придется изменить и перестроить рутину вызовов clone(2) , что явно не элегантно.

В Linux должны быть первоклассные системные вызовы fork() , vfork() , avfork() , thread_create() и container_create() . Семейство форков могло бы быть одним системным вызовом с параметрами, но потоки не являются процессами и не являются контейнерами (хотя контейнеры могут иметь процессы и могут иметь процесс minder/init). Объединение всего этого в один системный вызов кажется немного сложным, хотя даже это было бы нормально, если бы был только один флаг для записи контейнера/запуска/форка/любой метафоры, применяемой к контейнерам. Но дизайн clone(2) или его разработчики поощряют распространение флагов, что означает, что нужно постоянно обращать внимание на возможную необходимость добавления новых флагов в существующие места вызовов.

Мои друзья часто говорят мне, и я много где это встречаю, что «нет, контейнеры - это не зоны/jail’ы, они не предназначены для такого использования», но меня не волнуют эти аргументы. Миру нужны зоны/jail’ы, а контейнеры Linux действительно хотят быть зонами/jail’и. Да, хотят. Зоны/jail’ы должны начинать жизнь максимально изолированными, а совместное использование нужно добавлять явно с хоста. Делать это наоборот - плохо, потому что каждый раз, когда изоляция увеличивается, приходится патчить clone(2) вызовы. Это не лучший подход к безопасности для ОС, которая не интегрирована по принципу «сверху вниз» (в Linux у всего есть разные мейнтейнеры и сообщества: ядро, библиотеки C, каждая важная системная библиотека, shell’ы, система инициализации, все пользовательские программы - у всего). В таком мире контейнеры нужно начинать с максимальной изоляции.

Я мог бы продолжать. Я мог бы поговорить о безопасности форка. Я мог бы обсудить все функции, которые обычно или в определенных случаях безопасны для вызова в fork() , в сравнении с дочерним процессом vfork() , в сравнении с дочерним процессом afork() (если он у нас есть) или дочерним процессом вызова clone() (но мне пришлось бы рассмотреть довольно много комбинаций флагов!). Я мог бы рассказать, почему 4.4BSD удалил vfork() (хотя мне пришлось бы немного углубиться в тонкости). Я думаю, что длина этой статьи, вероятно, уже достигла оптимума, поэтому здесь я и остановлюсь.

Читайте также: