【问题标题】:Why do objects in parent/child processes have identical addresses?为什么父/子进程中的对象具有相同的地址?
【发布时间】:2017-02-09 08:32:47
【问题描述】:

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

守则

#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 调用了 sem_init,但似乎子进程应该获得信号量的副本。我没有看到sem 在父进程和子进程之间“共享”的机制:信号量没有命名,我不明白信号量是如何在父进程和子进程之间共享的。我怀疑每个进程只是简单地获取和释放它自己的信号量“副本”,但我不确定。

谢谢。

【问题讨论】:

    标签: c++ c process semaphore


    【解决方案1】:

    Linux 使用“copy-on-write”习语,这意味着在您调用fork() 之后,父级的内存不会立即复制(作为单独的副本)给子级。仅当子进程尝试将任何数据写入内存时才会发生复制。

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

    【讨论】:

    • 真的是因为copy on write吗?我的意思是,即使内存被立即复制,这会以任何方式影响地址吗?
    • @user2079303,我应该澄清一下。如果我们谈论物理地址,是的。如果我们谈论映射地址,可能不会。
    • @user2079303:这是因为这两个进程有不同的虚拟地址空间。两个不同进程中的两个相同地址可以映射到不同的物理内存。
    • @BlagovestBuyukliev 因此,我怀疑写入时复制不会影响 OP 打印的虚拟地址。
    • 应用程序永远无法看到物理地址。
    【解决方案2】:

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

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

    如果pshared 不为零,则信号量在进程之间共享,并且应该位于共享内存区域中

    这个在共享内存中的位置由你来处理,它不是自动为你完成的。

    【讨论】:

    • “共享内存中的这个位置由你决定” - 那么,在我的代码示例中,sem 并没有真正在父进程和子进程之间共享,这对吗?子进程'sem 只是父进程'sem 的一个副本,但它是一个不同的对象?
    • @StoneThrow 是的。我建议你read about POSIX shared memory
    【解决方案3】:

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

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

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

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

    参照。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-05
      • 1970-01-01
      相关资源
      最近更新 更多