【问题标题】:Does calling fork() in a multithreaded program block all threads?在多线程程序中调用 fork() 会阻塞所有线程吗?
【发布时间】:2021-10-30 23:24:52
【问题描述】:

在多线程程序中调用 fork() 会阻塞所有线程吗?还是只会阻塞调用线程?

('calling fork()'是指创建子进程的过程。)

【问题讨论】:

  • 你从哪里得到关于 fork 阻塞调用者的信息? Fork 不会阻塞任何东西。
  • 在多线程程序中分叉几乎总是一个错误。 linuxprogrammingblog.com/…
  • 在具有多个活动线程的应用程序中,分叉实际上是未定义的行为。
  • 有一个多线程应用程序中常用fork()的场景:允许exec(2)一个新程序在不同的进程中。为此,有必要使用 fork(),因为您需要一个新的虚拟空间,而 exec() 会完全覆盖它。如果您在多线程应用程序中执行 exec() 调用,您将看到所有线程都在死机。

标签: c++ c linux


【解决方案1】:

unix 中的术语进程类似于操作系统,它与执行线程的区别在于进程中的所有线程共享相同的虚拟空间内存。在其他方面,它们是完全不同的运行进程。每个都有自己的堆栈指针(对于用户内核模式,因为两个线程可以同时执行系统调用,并将它们的堆栈切换到内核堆栈,因此每个线程都有一个内核堆栈),因此没有理由进行系统调用一个线程阻塞或中断另一个线程中的系统调用,因为有可能两个不同的进程同时执行系统调用(除了明显的kill(2)read(2)write(2),这些最后一个阻塞仅当两次读取作用于同一个文件,该文件在系统调用序列化/序列读取/写入同一文件期间被锁定)

除此之外,fork() 调用是一个调用,在一个进程中开始,在两个进程中结束。 所以父进程中的 fork() 和子进程中的 fork 表现得好像两者都是同时启动的(如果您有每个呼叫起点的时间戳,您将拥有完全相同的时间戳,因为呼叫仅启动一次,并且需要复制时间戳)但是因为他们有两个不同的末端,它们可以是任何顺序,或者差异很小或很高(取决于系统负载)。没有什么能阻止 fork() 除了为孩子分配资源来启动(但是当孩子启动时,父母可能会离开很远,甚至死亡)。这可以使父进程在 fork() 之后继续(但不是在内核知道是否有足够的资源来构建子进程时,因为如果出现问题,fork 应该给父进程一个错误)很早在完整的系统调用中,它仍然(使用新线程)构建进程表条目、进程的用户区和它的第一个线程,然后返回0

正如您所知道的,所有与线程相关的东西都被“复制”到子进程中,但是由于子进程不是多线程的,因此没有线程可以使用该内存,但这也不是问题,因为进程不使用它,它实际上没有分配(在实际内存的页面中)(或者它是在交换空间中分配的,如果父级决定更改它,使用写时复制功能)

在多线程进程中创建 fork 的正常情况是当您需要执行另一个程序时。在这种情况下,fork() 将紧随其后的是 exec(),它将垃圾收集所有未使用的内存。这样想……如果你需要一个全新的虚拟空间,那么你需要fork……如果你想在线程之间共享内存,那就创建一个新线程。

【讨论】:

    【解决方案2】:

    在多线程程序中调用fork() 会阻塞所有线程吗?还是只会阻塞调用线程?

    fork() 函数本身不会阻止任何东西。特别是,它会在新的子进程启动后立即返回,而无需等待子进程终止。因此,除非孩子的生命非常短暂,否则在fork() 成功返回后,将会有两个进程在原来的位置运行。这就是“fork”这个名字的由来。

    当然,fork() 不是即时的,实际上它可能有点贵。调用它的线程在返回之前无法继续,因为这些是线程和函数调用的语义。同一进程中的其他线程不受影响,并且正如您的其他答案所观察到的,它们也不会传播到新进程。

    【讨论】:

    • 一旦知道子进程将被正确构建,它就可以返回。唯一可以阻止 fork() 返回的是在构建孩子时出错。子进程根本不需要运行,父进程从fork()返回
    【解决方案3】:
    • 子进程是用一个线程创建的——那个线程 称为 fork()。父级的整个虚拟地址空间 在孩子中复制,包括互斥体的状态, 条件变量和其他 pthreads 对象;指某东西的用途 pthread_atfork(3) 可能有助于处理问题 这可能会导致。

    • 在多线程程序中的 fork() 之后,子进程可以 仅安全调用异步信号安全函数(参见 signal-safety(7)) 直到它调用 execve(2)。

    https://man7.org/linux/man-pages/man2/fork.2.html

    【讨论】:

    • 你应该添加一个关于父线程没有被阻塞的声明。
    • 互斥锁的状态,但是互斥锁本身是不同的,因为它们保护非关联变量(原始保护父进程的变量和新的保护子进程的变量) pthread 对象存在,但没有线程正在为他们运行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-29
    • 2012-03-10
    相关资源
    最近更新 更多