分叉与穿线

我以前在应用程序中使用过线程,对它的概念非常了解,但是最近在操作系统讲座中我遇到了 fork ()。类似于线程。

我在谷歌上搜索了他们之间的区别,我得知:

  1. Fork 只不过是一个新进程,看起来与旧进程或父进程完全一样,但它仍然是一个不同的进程,具有不同的进程 ID 和自己的内存。
  2. 线程是开销较小的轻量级进程

但是,我心里还有一些疑问。

  1. 什么时候应该更喜欢 fork ()而不是 threading,反之亦然?
  2. 如果我想将外部应用程序作为子应用程序调用,那么应该使用 fork ()还是线程来调用它?
  3. 在谷歌搜索时,我发现人们说在线程中调用 fork ()是不好的。当人们做类似的事情时,为什么要在线程中调用 fork () ?
  4. Fork ()不能利用多处理器系统,因为父进程和子进程不能同时运行,这是真的吗?
64595 次浏览

如您所注意到的,fork()产生一个新的进程副本。上面没有提到的是经常跟随的 exec()调用。这将用一个新进程(一个新的可执行文件)替换现有进程,因此,fork()/exec()是从旧进程生成新进程的标准方法。

例如,您的 shell 就是这样从命令行调用进程的。您指定您的进程(比如说 ls)和 shell 分叉,然后执行 ls

请注意,这与线程处理在一个非常不同的级别上进行操作。线程在进程内运行多行执行。分叉是创建 新的进程的一种方法。

分叉方法和线程方法的主要区别在于操作系统体系结构之一。在 Unix 被设计出来的时候,forking 是一个简单易用的系统,能够最好地满足大型机和服务器类型的要求,因此它在 Unix 系统中得到了普及。当微软从头开始重新构建 NT 内核时,它更关注线程模型。就这一点而言,Unix 系统在分叉方面的效率与 Windows 在线程方面的效率之间仍然存在显著差异。您可以在 Apache 中看到这一点,Apache 在 Unix 上使用 prefork 策略,在 Windows 上使用线程池。

特别是你的问题:

什么时候应该更喜欢 fork ()而不是 threading,反之亦然?

在 Unix 系统上,您要执行的任务远比实例化一个 worker 复杂得多,或者您希望使用独立进程的隐式安全沙箱。

如果我想将外部应用程序作为子应用程序调用,那么应该使用 fork ()还是线程来调用它?

如果子级将使用相同的代码对父级执行相同的任务,则使用 fork。对于较小的子任务,使用线程。如果单独的外部进程两者都不使用,那么只需使用适当的 API 调用来调用它们。

在谷歌搜索时,我发现人们说在线程中调用 fork ()是不好的。当人们做类似的事情时,为什么要在线程中调用 fork () ?

不完全确定,但我认为复制一个进程和大量子线程在计算上是相当昂贵的。

Fork ()不能利用多处理器系统,因为父进程和子进程不能同时运行,这是真的吗?

这是错误的,fork 创建了一个新的进程,然后利用了 OS 任务调度程序中进程可用的所有特性。

一个分叉的过程被称为一个重量级的过程,而一个线程化的过程被称为轻量级进程。

以下是它们之间的区别:

  1. 分叉进程被认为是子进程,而线程化进程被称为兄弟进程。
  2. 分叉进程不与父进程共享代码、数据、堆栈等资源,而线程化进程可以共享代码,但有自己的堆栈。
  3. 进程切换需要操作系统的帮助,但不需要线程切换
  4. 创建多个进程是一个资源密集型任务,而创建多个线程是一个资源密集型任务
  5. 每个进程可以独立运行,而一个线程可以读/写另一个线程数据。 线程和进程 讲座 enter image description here

正如@243123412341234123412341234123所说,多亏了 COW,在 Linux 上,进程并不比线程重多少,归结起来就是它们的用法。COW-copy on write 意味着只有当分叉进程对其进行更改时,才会复制分叉进程的内存页面,否则操作系统会将其重定向到父进程的页面。

从一个编程用例来看,假设在堆内存中有一个大数据结构,一个2d 数组[2000000][100](200mb) ,内核的页面大小约为4kb。当进程分叉时,不会为此数组分配新的内存。如果一个特定的行(100字节)被更改(在父进程或子进程中) ,只有相应的页面(如果在两个页面中重叠,则为4kb 或8kb)将被复制并为分叉线程更新。

内存的其他部分在分叉进程中工作,与线程相同(代码相同,寄存器和调用堆栈是分开的)。

在 Windows 上,正如@Niels Kurentjes 所说,从性能角度来看,线程可能更好,但在 Linux 上,它更多的是用例。