Подтвердить что ты не робот

В частности, как 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(), но в ней особо не упоминалось динамически выделенная память в куче.

Спасибо

4b9b3361

Ответ 1

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

На самом деле, он не копируется с самого начала, он установлен на "Копировать-на-Запись", что означает, что один из процессов (родительский или дочерний) пытается изменить страницу, которую он скопировал, чтобы они не нанесли вреда одному - и все еще доступны все данные из точки fork().

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

Более подробная информация доступна здесь и здесь.

Ответ 2

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

Что касается документации: я заметил, что в документации обычно указывается, что все скопировано, кроме blah, blah blah.

Ответ 3

Короткий ответ "грязный при записи" - более длинный ответ - намного дольше.

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

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

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

Dw.