为什么父/子进程中的对象具有相同的地址?

时间:2017-02-09 08:32:47

标签: c++ c process semaphore

我对以下代码有两个问题:

守则

#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)

问题

从父级和子级进程打印时,为什么varsem的地址相同?我知道子进程得到父内存空间内容的副本,但我认为进程有独立且不同的地址空间,因此没有变量会在同一个内存位置 - 但这个输出似乎表明不是这样。

问题

这段代码实际上是在同步两个进程吗?我持怀疑态度。虽然我使用sem_init标志非零调用pshared,但似乎子进程应该是信号量的副本。我没有看到sem被共享的机制#34;父进程和子进程之间:信号量没有命名,我也不明白在父进程和子进程之间如何共享信号量。我怀疑每个过程只是简单地获取和发布自己的&#34; copy&#34;信号量,但我不确定。

谢谢。

3 个答案:

答案 0 :(得分:4)

Linux使用“copy-on-write”惯用法,这意味着在您调用which python后,父项的内存不会立即被复制(作为单独的副本)。只有子进程尝试将任何数据写入内存时才会发生复制。

理解“实际”内存地址(即物理内存中的地址)与映射地址(应用程序的内存空间中的地址)之间的区别也很重要。两个应用程序中的两个指针可能具有相同的值(虚拟地址),但这并不意味着它们确实指向相同的物理位置:Memory mapping

答案 1 :(得分:3)

关于地址,这是因为子进程最初是父进程的精确重复。精确复制包括(虚拟)内存映射。 Read the fork manual page了解更多信息。

关于信号量,如果你read the sem_init manual page,你会看到

  

如果pshared非零,那么信号量在进程之间共享,并且应该位于共享内存的区域中

这个位于共享内存中的位置由您来处理,但这不是为您自动完成的。

答案 2 :(得分:1)

除了SingerOfTheFall的答案之外,我想补充说fork(2)制作父进程的精确副本 - 相同的内存映射,相同的信号掩码,相同的文件描述符表 - 所以你实际上得到了一个真正的副本你的过程。

进程确实有不同的地址空间,但是为了理解为什么修改其中一个进程不会影响另一个进程,你应该记住虚拟和物理地址之间的区别以及所有进程(甚至是内核上的无论如何,amd64)在虚拟地址空间中执行。

长话短说 - 简而言之,CPU中有对应表(称为页表),每当您尝试访问给定地址时,CPU都会查找有问题地址的实际物理地址。内核为每个进程填充页表,并为每个进程提供相同的地址(如果未启用ASLR)。

我无法确定为什么父母和孩子之间共享信号量,但如果你的初始化是正确的,那么它就不应该从外部世界获取。

比照。

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

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