繁体   English   中英

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

[英]Why do objects in parent/child processes have identical addresses?

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

守则

#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的地址相同? 我知道子进程获取父内存空间内容的副本 ,但我认为进程有独立且不同的地址空间,因此没有变量会在同一个内存位置 - 但这个输出似乎表明不是这样。

问题

这段代码实际上是在同步两个进程吗? 我持怀疑态度。 虽然我用pshared标志非零调用sem_init ,但它似乎再次是子进程应该获取信号量的副本 我没有看到sem在父进程和子进程之间“共享”的机制:信号量没有命名,我不明白在父进程和子进程之间如何共享信号量。 我怀疑每个进程只是获取并释放自己的信号量“副本”,但我不确定。

谢谢。

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

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

关于地址,这是因为子进程最初是父进程的完全重复 精确复制包括(虚拟)内存映射。 阅读fork手册页以获取更多信息。

关于信号量,如果你阅读sem_init手册页,你会看到

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

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

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

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

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

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

参看

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM