Согласно Википедии (что может быть не так)
Когда выполняется системный вызов fork (), создается копия всех страниц, соответствующих родительскому процессу, загружаемая ОС в отдельную область памяти для дочернего процесса. Но это не нужно в определенных случаях. Рассмотрим случай, когда дочерний процесс выполняет
exec
системный вызов "" (который используется для выполнения любого исполняемого файла из программы на C) или завершается очень скоро послеfork()
. Когда дочерний элемент требуется просто для выполнения команды для родительского процесса, нет необходимости копировать страницы родительского процесса, поскольку онexec
заменяет адресное пространство процесса, который его вызвал, командой, которая должна быть выполнена.В таких случаях используется метод, называемый копирование при записи (COW). С помощью этой техники, когда происходит разветвление, страницы родительского процесса не копируются для дочернего процесса. Вместо этого страницы распределяются между дочерним и родительским процессами. Всякий раз, когда процесс (родительский или дочерний) изменяет страницу, для этого процесса (родительского или дочернего), который выполнил изменение, создается отдельная копия только этой конкретной страницы. Этот процесс будет использовать вновь скопированную страницу, а не общую страницу во всех будущих ссылках. Другой процесс (тот, который не изменял общую страницу) продолжает использовать оригинальную копию страницы (которая больше не является общей). Этот метод называется копированием при записи, поскольку страница копируется, когда какой-либо процесс записывает на нее.
Кажется, что когда один из процессов пытается записать на страницу, новая копия страницы выделяется и присваивается процессу, который вызвал ошибку страницы. Исходная страница впоследствии помечается как доступная для записи.
Мой вопрос: что произойдет, если fork()
get вызывается несколько раз, прежде чем какой-либо процесс попытается записать на общую страницу?
pmap -XX PID
илиcat /proc/PID/smap
.Ответы:
Ничего особенного не происходит. Все процессы совместно используют один и тот же набор страниц, и каждый получает свою личную копию, когда хочет изменить страницу.
источник
Поведение fork () зависит от того, имеет ли система * nix MMU или нет. В системе без MMU (например, в ранних PDP-11) системный вызов fork () копировал всю память родителя для каждого дочернего элемента. В * nix-системе на основе MMU ядро помечает все не-стековые страницы как R / O и разделяет их между родителем и потомком. Затем, когда любой процесс выполняет запись на любую страницу, MMU прерывает попытку, ядро затем выделяет доступную для записи страницу и обновляет таблицы страниц MMU, чтобы указывать на страницу, доступную для записи. Такое поведение копирования при записи обеспечивает ускорение, поскольку первоначально для каждого дочернего процесса необходимо выделять и клонировать только частный стек.
Если вы выполняете некоторый родительский код между каждым вызовом fork (), то результирующие дочерние процессы будут отличаться на страницах, которые были изменены родительским процессом. С другой стороны, если родитель просто выполняет несколько вызовов fork (), например, в цикле, тогда дочерние процессы будут практически идентичны. Если используется локальная переменная цикла, она будет отличаться в стеке каждого дочернего элемента.
источник
Когда система предварительно формирует форк, обычно (это может зависеть от реализации) она также помечает страницы как доступные только для чтения и отмечает родительский процесс как мастер этих страниц.
При попытке записи на эти страницы происходит сбой страницы, и ОС берет верх, копируя весь список страниц или только измененные (опять же, в зависимости от реализации), поэтому процесс записи будет иметь доступную для записи копию.
Когда есть несколько процессов, разветвленных от одного и того же, когда «главный» процесс записывает в свою память, другие процессы копируют свои эквивалентные страницы.
источник