сбивающий с толку системный вызов fork

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

Unix сделает точную копию адресного пространства родителя и передаст его потомку. Таким образом, у родительского и дочернего процессов есть отдельные адресные пространства.

#include <stdio.h>
#include <sys/types.h>

int main(void)
{
pid_t pid;
char y='Y';
char *ptr;
ptr=&y;
pid = fork();
if (pid == 0)
{
y='Z';
printf(" *** Child process  ***\n");
printf(" Address  is %p\n",ptr);
printf(" char value is %c\n",y);
sleep(5);
}
else
{
sleep(5);
printf("\n ***parent process ***\n",&y);
printf(" Address  is %p\n",ptr);
printf(" char value is %c\n",y);
}
}

вывод вышеуказанной программы:

 *** Child process  ***
 Address  is 69002894
 char value is Z

 ***parent process ***
 Address  is 69002894
 char value is Y

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

Пожалуйста, помогите мне понять это!


person Vijay    schedule 25.03.2010    source источник
comment
Имена переменных существуют только во время компиляции для определенного языка. Во время выполнения ваш код использует точный адрес (для стека он использует смещение от некоторого регистра, а указатели памяти перемещаются после того, как программа отображается в память). Кроме того, как указано в вашем примере - непосредственно перед тем, как разветвлять текущий процесс, вы храните указатель в каком-то месте (это даже может быть какое-то внешнее хранилище). Поэтому, если fork () вызовет изменение структуры памяти, ваша программа не сможет работать дальше после возврата из этого системного вызова.   -  person ony    schedule 25.03.2010
comment
Ответы правильные, но в вашем реальном примере есть еще одна проблема. Вы сохраняете адрес Y внутри ptr перед вилкой, поэтому ptr содержит «69002894». После вилки ptr по-прежнему будет содержать это значение как в родительском, так и в дочернем процессах, даже если адрес Y изменился бы в одном из двух процессов. Чтобы быть правильным, вы должны напечатать & Y в своем операторе printf.   -  person Laurent Parenteau    schedule 25.03.2010
comment
@laurent после вилки ptr также будет скопирован в дочерний процесс. это означает, что у дочернего и родительского будет два разных ptr. Пожалуйста, проверьте, изменив инструкции, как предложено вами в printf. вы получите тот же результат.   -  person Vijay    schedule 25.03.2010
comment
Да, я знаю, что вы получите тот же результат по причинам, указанным в ответах (виртуальное адресное пространство). Я имел в виду, что ЕСЛИ адрес y был бы другим в дочернем процессе (что все согласны с тем, что это не так), значение PTR все равно будет адресом Y в родительский процесс вместо адреса Y в дочернем процессе. Таким образом, вы не заметите разницы в ваших printfs.   -  person Laurent Parenteau    schedule 25.03.2010


Ответы (5)


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

Но на самом деле ОС предоставляет ему только виртуальную память, которая отображается в фактическую память ОС с помощью MMU.

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

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

Надеюсь, это поможет!!

person Furquan    schedule 25.03.2010

Ты прав. Как только вы вызываете fork(), существуют два идентичных процесса. Следовательно, адрес y одинаков для копии y в каждом процессе. Единственное различие между этими двумя процессами состоит в том, что в одном fork() вернул 0, а в другом он вернул PID дочернего процесса. Ваша программа использует эту информацию для различного поведения родительского и дочернего элементов, поэтому вы получаете соответствующий результат.

В «реальной жизни» операционные системы делают много оптимизаций, чтобы сделать fork() действительно быстрыми. Это означает, что реальное физическое поведение, вероятно, не связано с полной копией пространства памяти. Однако логически вы можете относиться к этому так же.

person Carl Norum    schedule 25.03.2010
comment
а как насчет комментария отдельных адресных пространств для дочернего и родительского? - person Vijay; 25.03.2010
comment
@benjamin, да, это разные процессы, поэтому у каждого есть собственное отдельное пространство виртуальной памяти. Они полностью независимы, как только fork() возвращается. - person Carl Norum; 25.03.2010
comment
Отдельные адресные пространства означают, что данный адрес в одном процессе относится к отдельному биту памяти от того же адреса в другом процессе. Вот как адрес 69002894 в вашем примере может одновременно хранить как X, так и Y - адрес 69002894 в процессе A отличается от адреса 69002894 в процессе B. - person caf; 25.03.2010

У них есть отдельное адресное пространство - именно поэтому один и тот же адрес памяти может иметь разные значения. Адрес памяти имеет значение только в контексте процесса.

person Billy ONeal    schedule 25.03.2010

Адрес 221 P Street - это отдельное здание от адреса 221 C. Street. Их содержимое различается, даже если у них одинаковый адресный номер.

person Windows programmer    schedule 25.03.2010

Вы должны заменить pid на ptr. Чтобы получить родительский и дочерний ID, вы должны использовать pid в printf(" Address is %p\n",pid); вместо ptr. После fork() программа выполняется в двух частях. Один ребенок, а другой родитель. Если вы вызываете адрес из переменной, которая использовалась до fork(), вы получите тот же результат как для родительского, так и для дочернего.

person Sabri Meviş    schedule 16.03.2013