В вычислениях , особенно в контексте операционной системы Unix и ее аналогов , fork - это операция, посредством которой процесс создает свою копию. Это интерфейс, который требуется для соответствия стандартам POSIX и Single UNIX Specification . Это, как правило , реализуется в виде C Стандартная библиотека (Libc) обертке до развилки, клон, или других системных вызовов в ядре . Форк - это основной метод создания процессов в Unix-подобных операционных системах.
Обзор
В многозадачных операционных системах процессам (запущенным программам) нужен способ создания новых процессов, например, для запуска других программ. Fork и его варианты обычно являются единственным способом сделать это в Unix-подобных системах. Чтобы процесс начал выполнение другой программы, он сначала создает копию самого себя. Затем копия, называемая « дочерним процессом », вызывает системный вызов exec, чтобы перекрыть себя другой программой: она прекращает выполнение своей предыдущей программы в пользу другой.
Операция fork создает отдельное адресное пространство для дочернего элемента. Дочерний процесс имеет точную копию всех сегментов памяти родительского процесса. В современных вариантах UNIX, которые следуют модели виртуальной памяти из SunOS-4.0, реализована семантика копирования при записи , и фактическое копирование физической памяти не требуется. Вместо этого страницы виртуальной памяти в обоих процессах могут ссылаться на одни и те же страницы физической памяти до тех пор, пока одна из них не запишет на такую страницу: затем она копируется. Эта оптимизация важна в общем случае, когда fork используется вместе с exec для выполнения новой программы: обычно дочерний процесс выполняет только небольшой набор действий, прежде чем он прекращает выполнение своей программы в пользу программы, которую нужно запустить, и для этого требуется очень мало родительских структур данных , если таковые вообще имеются .
Когда процесс вызывает fork, он считается родительским процессом, а вновь созданный процесс - его дочерним процессом. После вилки оба процесса не только запускают одну и ту же программу, но и возобновляют выполнение, как если бы оба вызвали системный вызов. Затем они могут проверить возвращаемое значение вызова, чтобы определить свой статус, дочерний или родительский, и действовать соответствующим образом.
История
Одна из самых ранних ссылок на концепции вилки появились в унипроцессоре System Design по Melvin Conway , опубликованной в 1962 году [1] статья Конвея мотивировано реализации по Л. Питер Дойч вилочки в системе разделения времени GENIE , где концепция была заимствован Кеном Томпсоном для его самого раннего появления [2] в Research Unix . [3] [4] Позже Fork стал стандартным интерфейсом в POSIX . [5]
Коммуникация
Дочерний процесс начинается с копии файловых дескрипторов своего родителя . [5] Для межпроцессного взаимодействия родительский процесс часто создает один или несколько каналов , а затем после разветвления процессы закрывают концы каналов, которые им не нужны. [6]
Варианты
Vfork
Vfork - это вариант fork с тем же соглашением о вызовах и почти такой же семантикой, но только для использования в ограниченных ситуациях. Он возник в версии Unix для 3BSD [7] [8] [9], первой Unix, поддерживающей виртуальную память. Он был стандартизирован POSIX, который позволял vfork иметь точно такое же поведение, как fork, но был помечен как устаревший в редакции 2004 года [10] и был заменен posix_spawn () (который обычно реализуется через vfork) в последующих редакциях.
Когда выполняется системный вызов vfork, родительский процесс будет приостановлен до тех пор, пока дочерний процесс либо не завершит выполнение, либо не будет заменен новым исполняемым образом через один из семейства системных вызовов " exec ". Дочерний элемент заимствует настройку MMU у родительского, и страницы памяти совместно используются родительским и дочерним процессами без копирования и, в частности, без семантики копирования при записи ; [10] следовательно, если дочерний процесс вносит изменения в любую из общих страниц, новая страница не будет создана, и измененные страницы также будут видны родительскому процессу. Поскольку при этом абсолютно не требуется копирование страниц (потребление дополнительной памяти), этот метод является оптимизацией по сравнению с обычным fork в средах с полным копированием при использовании с exec. В POSIX использование vfork для любых целей, кроме как прелюдия к немедленному вызову функции из семейства exec (и некоторых других операций), приводит к неопределенному поведению . [10] Как и в случае с vfork, дочерний элемент заимствует структуры данных, а не копирует их. vfork по-прежнему быстрее, чем вилка, использующая семантику копирования при записи.
Система V не поддерживала этот вызов функции до того, как была представлена система VR4, [ необходима цитата ], потому что совместное использование памяти, которое она вызывает, подвержено ошибкам:
Vfork не копирует таблицы страниц, поэтому он работает быстрее, чем реализация вилки System V. Но дочерний процесс выполняется в том же физическом адресном пространстве родительского процесса (до тех пор , Exec или выхода ) и могут , таким образом , перезаписать данные родителя и стеку. Опасная ситуация может возникнуть, если программист неправильно использует vfork , поэтому ответственность за вызов vfork лежит на программисте. Разница между подходом System V и подходом BSD носит философский характер: должно ли ядро скрывать особенности своей реализации от пользователей или оно должно предоставлять опытным пользователям возможность использовать преимущества реализации для более эффективного выполнения логических функций?
- Морис Дж. Бах [11]
Точно так же на странице руководства Linux для vfork настоятельно не рекомендуется его использование: [7] [ неудачная проверка ] [ обсудить ]
Очень жаль, что Linux возродил этот призрак из прошлого. На странице руководства BSD говорится: «Этот системный вызов будет удален, когда будут реализованы надлежащие механизмы совместного использования системы. Пользователи не должны зависеть от семантики разделения памяти vfork (), поскольку в этом случае он будет синонимом fork (2). . "
Другие проблемы с vfork включают взаимоблокировки, которые могут возникнуть в многопоточных программах из-за взаимодействия с динамической компоновкой . [12] В качестве замены vfork , POSIX представил Семейство функций posix_spawn , объединяющих действия fork и exec. Эти функции могут быть реализованы как библиотечные подпрограммы с точки зрения fork , как это сделано в Linux, [12] или в терминах vfork для лучшей производительности, как это сделано в Solaris, [12] [13], но в спецификации POSIX отмечается, что они были «разработаны как операции ядра », особенно для операционных систем, работающих на ограниченном оборудовании и системах реального времени . [14]
Хотя реализация 4.4BSD избавилась от реализации vfork, что привело к тому, что vfork ведет себя так же, как fork, позже он был восстановлен в операционной системе NetBSD по соображениям производительности. [8]
Некоторые встроенные операционные системы, такие как uClinux, не используют fork и реализуют только vfork, потому что они должны работать на устройствах, где копирование при записи невозможно реализовать из-за отсутствия MMU .
Rfork
Plan 9 операционная система, созданная конструкторами Unix, включает в себя вилку , но и вариант , называемый «rfork» , что позволяет мелкозернистый совместное использование ресурсов между родительскими и дочерними процессами, в том числе адресное пространство (за исключением стека сегмента, который уникальный для каждого процесса), переменные среды и пространство имен файловой системы; [15] это делает его унифицированным интерфейсом для создания как процессов, так и потоков внутри них. [16] И FreeBSD [17], и IRIX переняли системный вызов rfork из Plan 9, последний переименовал его в sproc. [18]
Клонировать
clone
- это системный вызов в ядре Linux, который создает дочерний процесс, который может совместно использовать части своего контекста выполнения с родительским. Подобно rfork FreeBSD и sproc от IRIX, клон Linux был вдохновлен rfork Plan 9 и может использоваться для реализации потоков (хотя программисты приложений обычно используют интерфейс более высокого уровня, такой как pthreads , реализованный поверх clone). Функция «раздельных стеков» из Plan 9 и IRIX была опущена, потому что (по словам Линуса Торвальдса ) она вызывает слишком много накладных расходов. [18]
Форкинг в других операционных системах
В первоначальном проекте операционной системы VMS (1977 г.) операция копирования с последующим изменением содержимого нескольких конкретных адресов для нового процесса, как при разветвлении, считалась рискованной. [ необходима цитата ] Ошибки в текущем состоянии процесса могут быть скопированы в дочерний процесс. Здесь используется метафора порождения процесса: каждый компонент структуры памяти нового процесса создается заново с нуля. Икра позже метафора была принята в операционных системах Microsoft (1993).
Компонент POSIX-совместимости VM / CMS (OpenExtensions) предоставляет очень ограниченную реализацию fork, в которой родительский элемент приостанавливается, пока выполняется дочерний элемент, а дочерний элемент и родительский элемент совместно используют одно и то же адресное пространство. [19] По существу это vfork обозначен как вилка . (Обратите внимание, что это относится только к гостевой операционной системе CMS; другие гостевые операционные системы виртуальных машин, такие как Linux, предоставляют стандартные функции вилки.)
Использование приложения
Следующий вариант программы Hello World демонстрирует механику системный вызов fork на языке программирования C. Программа разделяется на два процесса, каждый из которых решает, какие функции они выполняют, на основе возвращаемого значения системного вызова fork. Код шаблона, такой как включения заголовка , был опущен.
int main ( void ) { pid_t pid = вилка (); если ( pid == -1 ) { perror ( " ошибка вилки" ); выход ( EXIT_FAILURE ); } else if ( pid == 0 ) { printf ( "Привет от дочернего процесса! \ n " ); _exit ( EXIT_SUCCESS ); } else { int status ; ( void ) waitpid ( pid , & status , 0 ); } return EXIT_SUCCESS ; }
Ниже приводится анализ этой программы.
pid_t pid = fork ();
Первое заявление в основные вызовы системный вызов fork для разделения выполнения на два процесса. Возвращаемое значение вилка записывается в переменную типа pid_t , который является типом POSIX для идентификаторов процессов (PID).
если ( pid == -1 ) { perror ( " ошибка вилки" ); выход ( EXIT_FAILURE ); }
Минус один указывает на ошибку в fork : новый процесс не был создан, поэтому выводится сообщение об ошибке.
Если fork был успешным, теперь есть два процесса, каждый из которых выполняет основная функция с точки, где вилка вернулась. Чтобы процессы выполняли разные задачи, программа должна перейти к возвращаемому значению fork, чтобы определить, выполняется ли он как дочерний или родительский процесс.
иначе if ( pid == 0 ) { printf ( "Привет от дочернего процесса! \ n " ); _exit ( EXIT_SUCCESS ); }
В дочернем процессе возвращаемое значение отображается как ноль (что является недопустимым идентификатором процесса). Дочерний процесс печатает желаемое приветственное сообщение и завершает работу. (По техническим причинам POSIX Здесь необходимо использовать функцию _exit вместо стандарта C функция выхода .)
else { int status ; ( void ) waitpid ( pid , & status , 0 ); }
Другой процесс, родительский, получает от fork идентификатор процесса дочернего процесса, который всегда является положительным числом. Родительский процесс передает этот идентификатор в Системный вызов waitpid для приостановки выполнения до завершения дочернего процесса. Когда это произошло, родитель возобновляет выполнение и завершает работу с помощью заявление о возврате .
Смотрите также
- Вилочная бомба
- Вилка – exec
- выход (системный вызов)
- ждать (системный вызов)
- порождение (вычисление)
Рекомендации
- Рианна Найман, Линус (25 августа 2016 г.). «Заметки по истории развилки и соединения». IEEE Annals of the History of Computing . 38 (3): 84–87. DOI : 10.1109 / MAHC.2016.34 .
- ^ "s3.s от Research UNIX" . GitHub . 1970 г.
- ^ Кен Томпсон и Деннис Ричи (3 ноября 1971 г.). "SYS FORK (II)" (PDF) . Руководство программиста UNIX . Bell Laboratories .
- ^ Ричи, Деннис М .; Томпсон, Кен (июль 1978 г.). «Система разделения времени UNIX» (PDF) . Bell System Tech. Дж . AT&T. 57 (6): 1905–1929. DOI : 10.1002 / j.1538-7305.1978.tb02136.x . Проверено 22 апреля 2014 года .
- ^ a b - Справочник по системным интерфейсам, спецификация Single UNIX , выпуск 7 от The Open Group
- ^ - Справочник по системным интерфейсам, Единая спецификация UNIX , выпуск 7 от The Open Group
- ^ a b - Руководство программиста Linux - Системные вызовы
- ^ а б «Документация по NetBSD: зачем реализовывать традиционный vfork ()» . Проект NetBSD . Проверено 16 октября 2013 года .
- ^ "vfork (2)". Руководство программиста UNIX, версия Virtual VAX-11 . Калифорнийский университет в Беркли. Декабрь 1979 г.
- ^ a b c - Справочник по системным интерфейсам, спецификация Single UNIX , выпуск 6 из The Open Group
- ^ Бах, Морис Дж. (1986). Дизайн операционной системы UNIX . Прентис-Холл. С. 291–292. Bibcode : 1986duos.book ..... B .
- ^ а б в Нахимовский, Грег (2006). «Минимизация использования памяти для создания подпроцессов приложений» . Технологическая сеть Oracle . Корпорация Oracle .
- ^ Реализация Posix_spawn () OpenSolaris: https://sourceforge.net/p/schillix-on/schillix-on/ci/default/tree/usr/src/lib/libc/port/threads/spawn.c
- ^ - Справочник по системным интерфейсам, Единая спецификация UNIX , выпуск 7 от The Open Group
- ^ - Руководство программиста Plan 9 , том 1
- ^ - Руководство программиста Plan 9 , том 1
- ^ - Руководство по системным вызовам FreeBSD
- ^ а б Торвальдс, Линус (1999). «Край Linux» . Открытые источники: голоса революции открытого исходного кода . О'Рейли. ISBN 978-1-56592-582-3.
- ^ «z / VM> z / VM 6.2.0> Программирование приложений> z / VM V6R2 OpenExtensions Документ соответствия POSIX> Документ соответствия POSIX.1> Раздел 3. Примитивы процессов> 3.1 Создание и выполнение процессов> 3.1.1 Создание процесса» . IBM . Проверено 21 апреля 2015 года .