В частности, как fork() обрабатывает динамически выделенную память из malloc() в Linux?
У меня есть программа с родительским и дочерним процессом. Перед fork() родительский процесс называется malloc() и заполняет массив некоторыми данными. После fork() ребенку нужны эти данные. Я знаю, что могу использовать трубку, но следующий код работает:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main( int argc, char *argv[] ) {
char *array;
array = malloc( 20 );
strcpy( array, "Hello" );
switch( fork() ) {
case 0:
printf( "Child array: %s\n", array );
strcpy( array, "Goodbye" );
printf( "Child array: %s\n", array );
free( array );
break;
case -1:
printf( "Error with fork()\n" );
break;
default:
printf( "Parent array: %s\n", array );
sleep(1);
printf( "Parent array: %s\n", array );
free( array );
}
return 0;
}
Вывод:
Parent array: Hello
Child array: Hello
Child array: Goodbye
Parent array: Hello
Я знаю, что данные, выделенные в стеке, доступны в дочернем элементе, но кажется, что данные, выделенные в куче, также доступны для дочернего элемента. Аналогичным образом, ребенок не может изменять родительские данные в стеке, ребенок не может изменять родительские данные в куче. Поэтому я полагаю, что у ребенка есть своя копия данных стека и кучи.
Это всегда так в Linux? Если да, то где это документация, которая поддерживает это? Я проверил man-страницу fork(), но в ней особо не упоминалось динамически выделенная память в куче.
Спасибо
Ответы
Ответ 1
Каждая страница, выделенная для процесса (будь то страница виртуальной памяти, в которой есть стек или куча), копируется для разветвленного процесса, чтобы иметь доступ к ней.
На самом деле, он не копируется с самого начала, он установлен на "Копировать-на-Запись", что означает, что один из процессов (родительский или дочерний) пытается изменить страницу, которую он скопировал, чтобы они не нанесли вреда одному - и все еще доступны все данные из точки fork().
Например, страницы кода, которые фактический исполняемый файл был отображен в память, обычно доступны только для чтения и, следовательно, повторно используются среди всех разветвленных процессов - они не будут скопированы снова, так как там никто не пишет, только читать, и поэтому копирование по-записи никогда не понадобится.
Более подробная информация доступна здесь и здесь.
Ответ 2
После вилки ребенок полностью независим от родителя, но может наследовать некоторые вещи, которые являются копиями родителя. В случае кучи ребенок будет концептуально иметь копию кучи родителей во время вилки. Тем не менее, изменения в голове в дочернем адресном пространстве будут изменять только дочернюю копию (например, путем копирования на запись).
Что касается документации: я заметил, что в документации обычно указывается, что все скопировано, кроме blah, blah blah.
Ответ 3
Короткий ответ "грязный при записи" - более длинный ответ - намного дольше.
Но для всех намеревающихся и целей - рабочую модель, которая на уровне C безопасна, состоит в том, что сразу после fork() оба процесса абсолютно идентичны - то есть ребенок получает 100% точную копию (но для бит, бит вокруг возвращаемого значения fork()), а затем начать расходиться, поскольку каждая сторона изменяет свою память, стек и кучи.
Итак, ваш вывод немного неактивен - ребенок начинает с тех же данных, что и родительский, скопированный в свое собственное пространство, - затем модифицирует его - и видит его как измененный - в то время как родительский файл продолжает свою собственную копию.
В действительности все немного сложнее - поскольку он пытается избежать полной копии, делая что-то грязное; избегая копирования до тех пор, пока это не произойдет.
Dw.