【问题标题】:Are threads copied when calling fork?调用fork时是否复制线程?
【发布时间】:2020-07-27 03:40:15
【问题描述】:

如果我有一个使用线程运行的程序并在基于 unix 的系统上调用 fork(),是否复制了线程?我知道当前进程的虚拟内存被 1:1 复制到生成的新进程中。我知道线程在进程的虚拟内存中有自己的堆栈。因此,至少也应该复制线程堆栈。但是,我不知道是否有更多的线程不驻留在虚拟内存中,因此不会被复制。如果没有,这两个进程是共享线程还是独立副本?

【问题讨论】:

    标签: c multithreading unix process fork


    【解决方案1】:

    没有。

    线程不会复制到fork()。 POSIX 规范说(强调是我的):

    fork - 创建一个新进程

    应使用单个线程创建进程。如果多线程进程调用 fork(),则新进程应包含调用线程及其整个地址空间的副本,可能包括互斥锁和其他资源的状态。因此,为避免错误,子进程只能执行异步信号安全操作,直到调用其中一个 exec 函数。

    为了规避这个问题,有一个pthread_atfork() 函数可以提供帮助。

    【讨论】:

      【解决方案2】:

      最初,“fork”是通过将任务写入磁盘然后而不是在不同的线程中读取(如果将任务与不同的任务交换来完成)来实现的,修改图像的任务 ID 仍在内存并继续执行(作为新任务)。这是对基本任务切换机制的一个非常简单的修改,一次只有一个任务会占用 RAM 内存。

      当然,随着内存管理变得更加精细,这个方案也进行了修改以适应新环境。

      【讨论】:

      • 很好奇为什么这被否决了。这就是 Unix 的做法。
      • 这是一个有趣的见解,但它在哪里提到线程?看起来不像是我的答案。
      【解决方案3】:

      来自 The Open Group Base Specification 第 7 期,2018 版的fork

      应使用单个线程创建进程。如果多线程进程调用fork(),新进程应包含调用线程及其整个地址空间的副本,可能包括互斥锁和其他资源的状态。因此,为避免错误,子进程只能执行异步信号安全操作,直到调用 exec 函数之一。

      当应用程序从信号处理程序调用 fork() 并且pthread_atfork() 注册的任何 fork 处理程序调用非异步信号安全的函数时,行为未定义。

      【讨论】:

        【解决方案4】:

        man fork:

        子进程是使用单个线程创建的——调用 fork() 的线程。父级的整个虚拟地址空间在子级中复制,包括互斥锁、条件变量和其他 pthreads 对象的状态;使用 pthread_atfork(3) 可能有助于处理这可能导致的问题。

        【讨论】:

        • 但这似乎很奇怪:如果实际线程(我不知道其中包含虚拟内存以外的存储空间)不是,为什么要复制调用 fork 进程中线程的堆栈?
        • 嗯,它的为什么是一个完全不同的问题。我不知道导致该实施的原始设计决策。如果您有兴趣,您应该将其作为一个单独的问题提出。
        • @Jean-BaptisteYunès “但其他线程堆栈没有被复制”我不认为我这么说。我想知道如果实际执行的线程(启动上下文切换的线程等等)不是,为什么将位于虚拟内存中的线程堆栈复制到新进程(因为复制了虚拟内存)。那么似乎浪费内存:/
        • @Jean-BaptisteYunès 在 unix 系统中,有一个结构表示进程的虚拟内存。那是复制的那个。不仅仅是堆和 bss
        • 你得到了整个内存空间——因此得到了所有线程的堆栈。您需要它,因为对于剩余线程可访问的堆栈(或静态内存)中的指针指向的位置没有限制 - 它们很可能指向原始进程中某个线程堆栈中的数据
        猜你喜欢
        • 2018-05-08
        • 1970-01-01
        • 2011-05-20
        • 2018-06-17
        • 2014-10-24
        • 1970-01-01
        • 2019-05-05
        • 2014-08-08
        • 2020-03-13
        相关资源
        最近更新 更多