分叉一个不使用它自己的内存副本的子进程

Posted

技术标签:

【中文标题】分叉一个不使用它自己的内存副本的子进程【英文标题】:Forking a child process that does not use it´s own memory copy 【发布时间】:2016-08-05 15:46:42 【问题描述】:

我想要实现的是: 生成一个新的子进程 (pchild),它不使用自己的,而是使用其父进程 (pparent) 的内存块。

我为什么要实现这种行为: 考虑多个测试,其中第一个导致 segvault。 通常,由于段错误,您的进程会在此处停止,所有其他测试将不再执行。因此,我想将每个测试封装在自己的进程中。

主要问题: 一旦我产生了一个进程,它就会获得自己的内存副本(嗯,我是 意识到由于“写入时复制”技术,并非所有操作系统都完全正确)。想想例如测试树功能,其中我有一个节点结构,它有两个指向其他节点的指针。一旦我通过例如检索节点使用管道或某些共享内存块,这些指针指向一个地址,该地址是 pchild 内存块的一部分,因此当我尝试从 pparent 通过跟随节点结构内的指针来获取子节点时,我会得到一个 segvault。

线程没有用,因为一旦发生段错误,某些操作系统的主要行为。 (由于“状态不明”而杀死孩子和父亲)。

到目前为止我所拥有的(仅分叉测试部分):

int main (void) 
    // forking
    pid_t pid = fork();
    if (pid < 0 ) 
        // somewhat went wrong
        printf("An error occured!");
     else if (pid != 0)                               // inside parent
        // closing writing end, as not needed
        if(wait(NULL)!=0)
            printf("Segfault in Child\n");
         else 
            printf("Everyone is done!\n");
        
     else 
        printf("Child forked");
        char *s = (char *)0xDEADBEEF;
        *s = 'a';
        printf("this probally is never executed due to segfault\n");
    
    return 0;

现在我的想法是尝试让 pchild 只访问 pparent 的内存段。 我欢迎任何关于如何做到这一点的想法。 问候, 拉尔斯

【问题讨论】:

您确定需要这个吗?如果一项测试崩溃,那么运行进一步测试有什么意义? 如果你在 Linux 上,你可以通过clone() 来实现这个疯狂的计划。 fork_and_exec gdb 并将其附加到主进程。 实际上,我真的不明白为什么在父内存的精确副本上进行测试与“在父内存上进行测试”有很大不同 【参考方案1】:

你的问题不是很清楚,但我想你有一个XY problem。

我的理解是,您希望运行一系列测试,其中每个测试都会看到以前成功的测试的结果,但不会看到崩溃/失败的测试的结果。如果是这种情况,一种方法是:

    每次测试前fork 在孩子身上执行测试。 如果测试成功,让父级退出或等待子级再次分叉并在其子级中执行下一个测试。如果测试失败,请再次让父 fork 进行下一次测试。

另一种方法可能是将您的数据结构仅保存在由mmapMAP_SHARED|MAP_ANON 分配的共享内存中,但是如果一个测试使它们处于不一致的状态,那么所有未来的测试结果都会垃圾。

您在进程之间共享所有内存的想法在技术上是可行的,但它会立即崩溃,因为它们会破坏彼此的状态。

【讨论】:

嗯,这个答案没那么有用。首先,第 1 步和第 2 步是我想做的事情。它的问题是什么,正在获得一个不错的返回值。由于父母和孩子有单独的内存,返回一个结构,其中包括从孩子到父母的指针(并在孩子中分配)会搞砸一切。因为这些指针会导致内存,一旦我在父母中跟随它们,我就会得到一个段错误...... 大部分测试不会相互建立! - 忘了说,对不起! @LarsPrehn:如果您只想将结果从子级返回给父级,请通过管道发送它们或使用共享的 mmap。这就是为什么我说你有一个 XY 问题 - 你问的是如何进行非常复杂的 hack,以使用具有明确定义的行为的标准工具轻松实现一些目标。 正如上面问题中已经提到的:通过共享内存或使用管道重新调整结构可以让我传输结构,但是它们内部的指针指向子空间(因为它们是在子空间中分配的)所以如果我让它们回到父级并想要访问指针我试图到达一个不在我的父级内存块内的内存位置......因此我得到一个段错误。 @LarsPrehn:是的。要使用管道,您需要序列化数据并在接收端对其进行反序列化。如果您使用共享内存,您可以将所有内容(以及指向的数据)放入共享内存分配中。【参考方案2】:

每个进程都有自己的virtual memory 地址空间,因此无法将 RAM 地址分配给另一个进程(您可以发送一个数字,但在另一个virtual memory 地址空间中,它将指向不同的“真实”地点)。

您只需要另一个线程。同一进程的线程共享一个virtual memory空间。

【讨论】:

“每个进程都有自己的虚拟内存地址空间,因此无法将 RAM 地址分配给另一个进程” - 绝对不是真的!请参阅 mmap ...“您只需要另一个线程。同一进程的线程共享一个虚拟内存空间。” ...正如我在问题中所述,线程根本没有帮助。如果我不知道这一点,我怎么能提出这个问题?【参考方案3】:

尝试使用 vfork();而不是 fork();系统调用。 共享父子进程的资源。

【讨论】:

鉴于孩子在vfork() 之后可能执行的唯一允许操作是_exit()exec(),目前还不清楚它对 OP 有何帮助。

以上是关于分叉一个不使用它自己的内存副本的子进程的主要内容,如果未能解决你的问题,请参考以下文章

分叉的子进程是不是使用相同的信号量?

创建一个不是创建进程子进程的新进程

关于wait 和 exit

Linux学习-进程管理

fork() 并释放所有分配的内存

golang 热重启