2017-02-09 8 views
2

У меня есть два вопроса о следующем коде:Почему объекты в родительских/дочерних процессах имеют одинаковые адреса?

Кодекса:

#include <unistd.h> 
#include <semaphore.h> 
#include <iostream> 

int main(int argc, char **argv) 
{ 
    sem_t sem; 
    int var = 0; 

    /* create, initialize semaphore */ 
    if(sem_init(&sem,1,1) < 0) 
    { 
     perror("semaphore initilization"); 
     exit(0); 
    } 

    int pid = fork(); 
    static const size_t loopLen = 5; 
    if (0 == pid) 
    { /* child process */ 
     for (size_t i = 0; i < loopLen; ++i) 
     { 
      sem_wait(&sem); 
      std::string str("Child"); 
      std::cout << str << " process: &var(" << (void*)(&var) << ") var(" << var++ << ") &sem(" << (void*)(&sem) << ")" << std::endl; 
      sem_post(&sem); 
     } 
    } 
    else 
    { /* parent process */ 
     for (size_t i = 0; i < loopLen; ++i) 
     { 
      sem_wait(&sem); 
      std::string str("Parent"); 
      std::cout << str << " process: &var(" << (void*)(&var) << ") var(" << var++ << ") &sem(" << (void*)(&sem) << ")" << std::endl; 
      sem_post(&sem); 
     } 
    } 
} 

Выход:

Parent process: &var(0xffffcbdc) var(0) &sem(0xffffcbe0) 
Child process: &var(0xffffcbdc) var(0) &sem(0xffffcbe0) 
Parent process: &var(0xffffcbdc) var(1) &sem(0xffffcbe0) 
Child process: &var(0xffffcbdc) var(1) &sem(0xffffcbe0) 
Parent process: &var(0xffffcbdc) var(2) &sem(0xffffcbe0) 
Child process: &var(0xffffcbdc) var(2) &sem(0xffffcbe0) 
Parent process: &var(0xffffcbdc) var(3) &sem(0xffffcbe0) 
Child process: &var(0xffffcbdc) var(3) &sem(0xffffcbe0) 
Parent process: &var(0xffffcbdc) var(4) &sem(0xffffcbe0) 
Child process: &var(0xffffcbdc) var(4) &sem(0xffffcbe0) 

Вопрос:

Почему адр eses var и sem то же самое при печати от родительского и дочернего процессов? Я знаю, что дочерние процессы получают экземпляр содержимого пространства памяти родителя, но я думал, что процессы имеют отдельные и разные адресные пространства, и, следовательно, никакие переменные не будут находиться в одном и том же месте памяти, но этот вывод, как представляется, указывает на другое.

Вопрос:

Является ли этот код на самом деле синхронизации двух процессов? Я настроен скептически. Хотя я назвал sem_init флагом pshared отличным от нуля, снова кажется, что дочерний процесс должен получать копию семафора. Я не вижу механизма, с помощью которого sem «разделяется» между родительским и дочерним процессом: семафор не назван, и я не понимаю, как еще семафор получает общий доступ между родительским и дочерним процессом. Я подозреваю, что каждый процесс просто приобретает и выпускает свою собственную «копию» семафора, но я не уверен.

спасибо.

ответ

3

Что касается адресов, это связано с тем, что дочерний процесс начинается как точный дубликат родительского процесса. Точное дублирование включает (виртуальную) карту памяти. Read the fork manual page для получения дополнительной информации.

Что касается семафора, если вы read the sem_init manual page вы увидите, что

Если pshared отличен от нуля, то семафор разделяется между процессами, и должны быть расположены в области разделяемой памяти

Это место размещения в общей памяти зависит от вас, но это не то, что автоматически делается для вас.

+0

«Это место размещения в разделяемой памяти зависит от вас». Правильно ли, что в моем примере кода «семя» не разделяется между родительским и дочерним процессом? Детский процесс '' sem' является просто копией - но отдельным объектом - родительского процесса '' sem'? – StoneThrow

+0

@StoneThrow Это правда. Я предлагаю вам [прочитать о общей памяти POSIX] (http://man7.org/linux/man-pages/man7/shm_overview.7.html). –

4

Linux использует «идиома« copy-on-write », что означает, что после вызова fork() память родителя не сразу копируется (как отдельная копия) для ребенка. Копия произойдет только тогда, когда дочерний процесс попытается записать любые данные в память.

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

+0

Является ли это на самом деле из-за копирования на записи? Я имею в виду, что даже если бы память была скопирована немедленно, это повлияло бы на адреса? – user2079303

+1

@ user2079303, я должен, вероятно, уточнить это. Если говорить о физических адресах, да. Если говорить о сопоставленных адресах, возможно, нет. – SingerOfTheFall

+2

@ user2079303: это связано с тем, что два процесса имеют различные виртуальные адресные пространства. Два равных адреса в двух разных процессах могут отображаться в другой физической памяти. –

1

В дополнение к ответу SingerOfTheFall, я хотел бы добавить, что fork(2) делает точную копию родительского процесса - такие же карты памяти, те же сигнальные маски, одну и ту же таблицу дескрипторов файлов, чтобы вы действительно получили истинную копию своего процесса.

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

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

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

Cf.

https://en.wikipedia.org/wiki/Virtual_address_space

https://en.wikipedia.org/wiki/Page_table

Смежные вопросы