Смущает системный вызов fork () - PullRequest
1 голос
/ 18 января 2011

Я создал родительский и дочерний процессы с помощью fork (), и оба используют общий адрес памяти с именем «ptr».Но я запутался из-за одного выхода программы:

1) Адрес ptr: 123456 ПРИМЕЧАНИЕ. Один и тот же адрес для родительского и дочернего элементов, поэтому ожидается, что если один процесс изменит этот адрес, он должен отразить длядругой тоже процесс, так как адрес тот же.

2) Родитель: * ptr = 44

3) Дочерний: * ptr = 33

4) Значения печати:Родитель по-прежнему сохраняет старое значение: printf ("ptr =% d", * ptr);// Вывод: по-прежнему 44, exp равно 33. Печатает 33, ожидаемое значение.printf ("ptr =% d", * ptr); // печатает 33 штрафа

Вопрос1 ) Может кто-нибудь сказать мне, как значения отличаются?Хотя адрес указателя одинаков как для родительского, так и для дочернего элементов?

Question2 ) Я работаю над инструментом утечки памяти, который дает двойное освобождение, ошибка, так как он видит родителя иребенок бесплатно по тому же адресу.Однако, как мы видим, это не случай двойного освобождения.Как разобраться в этой проблеме?Что касается адреса памяти, который инструмент видит для родительского, а дочерний - тот же адрес?

PS: см. Фрагмент кода ниже:

#include <sys/types.h>
#include <unistd.h>
#include <cstdlib>
int main()
{
 int pid, *ptr
 ptr=(int*)malloc(sizeof(int));
 *ptr=33; // Parent keeps the data as 33, before forking.

 if(pid==0){*ptr=44;} // Child modifies data, which is ignored by parent

 // Now we print the memory address and the value both by child and parent
  if(pid==0)
  {
    printf("Child data: %u\n",*ptr);
    printf("Child address: %u\n",ptr);
  }
  if(pid>0)
  {
    printf("Parent data: %u\n",*ptr);
    printf("Parent address: %u\n",ptr);
  }
}

Вывод: дочерние данные: 44 дочерний адрес: 123456

Родительские данные: 33 (почему все еще старое значение?) Родительский адрес: 123456 (Почему тот же адрес, но данные отличаются от дочерних?)

Ответы [ 3 ]

5 голосов
/ 18 января 2011
if(pid==0){*ptr=44;} // Child modifies data, which is ignored by parent
Вопрос1) Кто-нибудь может подсказать, чем отличаются значения? Хотя адрес указателя одинаков как для родительского, так и для дочернего элементов?

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

См. Статью Википедии о таблицах страниц и аналогичные темы для некоторых иллюстраций того, как это работает.

- (долго следует) -

Что обычно происходит на fork(), так это то, что таблицы страниц как для родительского, так и для дочернего элементов настроены так, что страницы помечаются как доступные только для чтения. Когда для некоторого местоположения происходит инструкция записи, ядро ​​получает ошибку page , которую ЦП генерирует при плохом доступе к памяти. Ядро выделит новую память для захваченного процесса, сопоставит ее с нужным адресом путем манипулирования таблицей страниц, скопирует старый буфер во вновь выделенный и позволит продолжить запись. Это называется копирование при записи . Это делает начальный разветвление быстрым и снижает потребление памяти для страниц, которые не записаны ни в одном из процессов.

Предыдущий абзац - это всего лишь оптимизация модели программирования форка. Говорят, что ранние Unix не делали этого - они делали полную память всего процесса. Я также слышал, что Cygwin's fork() делает полную копию.

Но виртуальный адрес не имеет ничего общего с физическим адресом памяти. Процессор использует его как «ключ» для таблицы страниц, которая определяет, где находится фактическая память. Таблица страниц также может указывать, что страница недействительна, и в этом случае ядро ​​имеет возможность выполнить «исправление» (выполнить копирование при записи, вызвать страницу из пространства подкачки и т. Д.) Или завершить процесс в случай недопустимого доступа к указателю.

2 голосов
/ 18 января 2011

Вы неправильно поняли, как работает память в Unix-подобной системе: память родителя и потомка независимы.Если вы хотите, чтобы они общались, вы можете настроить явно разделяемую память или IPC.

0 голосов
/ 18 января 2011

Даже если вы думаете о памяти как о большом буфере с отдельными адресами, это еще не все.

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

При запуске программы на языке C в пространстве пользователя(или даже программа, написанная на ассемблере), к которой вы обращаетесь - это виртуальная память, а адреса - это адреса виртуальной памяти.Для простоты компиляторов и загрузчиков программ в современной операционной системе каждый процесс имеет свое независимое адресное пространство памяти, а адресные пространства не связаны друг с другом (например, если каждый процесс может получить доступ ко всему пространству памяти машины).Конечно, если доступ к какой-либо странице виртуальной памяти процесса не сопоставлен с физической памятью (или заменен на диск), это вызовет «ошибку сегментации».

Когда процесс создается с использованием fork, пространство памятипапа дублируется у ребенка (то есть: одни и те же данные по одинаковым виртуальным адресам для обоих).После разветвления они будут расходиться при изменении памяти в одном процессе, а не в другом.Реальный механизм немного сложнее, обычно это копирование при записи, когда на странице памяти выполняется модификация, выполняется копирование этой страницы, если не было внесено никаких изменений, два процесса могут получить доступ для чтения в одной физической памяти.,Это объясняет то, что вы видите при изменении значений в родительском дочернем процессе: вы видите, что он был помещен перед разветвлением (общее для обоих процессов) или разные значения, если он был изменен после разветвления.друг с другом вы должны использовать некоторый коммуникационный уровень (сокет, файл, канал, разделяемая память и т. д.).И не верьте, что использование разделяемой памяти особенно просто и быстрее, чем другие методы, это неправда.

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

Пространство совместно используемой памяти также в основном верно для программирования на уровне ядра, но в любом случае fork не будет доступен.

...