18

我是这方面的初学者。

我研究过fork(),vfork()clone()pthreads 。

我注意到这pthread_create()将创建一个线程,这比使用fork(). 此外,线程将与父进程共享文件描述符、内存等。

但是什么时候fork()clone()pthreads 更好?你能通过给出现实世界的例子向我解释一下吗?

提前致谢。

4

4 回答 4

36

clone(2)是 Linux 特定的系统调用,主要用于实现线程(特别是用于pthread_create)。使用各种参数,clone也可以具有类似fork(2)的行为。很少有人直接使用clone,使用pthread库更便携。clone(2)只有在实现自己的线程库(Posix 线程的竞争对手)时,您才可能需要直接调用syscall,这非常棘手(特别是因为锁定可能需要在机器调整的汇编编码例程中使用futex(2) syscall ,见futex(7) )。您不想直接使用clonefutex因为 pthread 使用起来要简单得多。

libpthread.so(其他 pthread 函数需要在 a cloneduring a之后在内部完成一些簿记pthread_create

正如Jonathon回答的那样,进程有自己的地址空间和文件描述符集。并且一个进程可以使用execve系统调用执行一个新的可执行程序,它基本上初始化地址空间、堆栈和寄存器以启动一个新程序(但文件描述符可以保留,除非使用close-on-exec标志,例如O_CLOEXEC通过打开)。

在类Unix系统上,所有进程除了initpid 1的第一个进程,通常是forkvforkclonefork

(从技术上讲,在 Linux 上,您可以忽略一些奇怪的异常,特别是内核进程或线程以及一些罕见的内核启动的进程启动,例如/sbin/hotplug....)

forkexecve系统调用是 Unix 进程创建的核心(带有waitpid相关的系统调用)。

多线程进程有多个线程(通常由 创建pthread_create),它们都共享相同的地址空间和文件描述符。当您想在同一地址空间内并行处理相同数据时,您可以使用线程,但是您应该关心同步和锁定。阅读pthread 教程了解更多信息。

我建议您阅读一本好的 Unix 编程书籍,例如Advanced Unix Programming和/或(免费提供)Advanced Linux Programming

于 2012-07-26T05:28:57.943 回答
14

(和公司)的优势和劣势fork在于他们创建了一个新流程,该流程是现有流程的克隆。

这是一个弱点,因为正如您所指出的,创建一个新流程会产生相当多的开销。这也意味着进程之间的通信必须通过一些“批准的”通道(管道、套接字、文件、共享内存区域等)来完成。

这是一种优势,因为它在父母和孩子之间提供了(更多)更大的隔离。例如,如果一个子进程崩溃,您可以将其杀死并相当容易地启动另一个。相比之下,如果一个子线程死了,杀死它充其量是有问题的——不可能确定该线程独占的资源,所以你不能在它之后进行清理。同样,由于进程中的所有线程共享一个公共地址空间,遇到问题的一个线程可能会覆盖所有其他线程正在使用的数据,因此仅仅杀死一个线程不一定足以清理混乱.

换句话说,使用线程有点像赌博。只要您的代码都是干净的,您就可以通过在单个进程中使用多个线程来获得一些效率。使用多个进程会增加一些开销,但可以使您的代码更加健壮,因为它限制了单个问题可能造成的损害,并且可以在进程遇到重大问题时轻松关闭和替换进程问题。

就具体示例而言,Apache 可能是一个相当不错的例子。它将为每个进程使用多个线程,但为了限制出现问题时的损害(除其他外),它限制了每个进程的线程数,并且可以/将产生多个同时运行的单独进程。例如,在一台体面的服务器上,您可能有 8 个进程,每个进程有 8 个线程。大量线程有助于它在一个主要受 I/O 绑定的任务中为大量客户端提供服务,并且将其分解为多个进程意味着如果确实出现问题,它不会突然变得完全无响应,并且可以关闭并重新启动一个过程而不会损失很多。

于 2012-07-26T05:42:33.543 回答
6

这些是完全不同的事情。fork()创建一个新进程pthread_create()创建一个新线程,该线程在同一进程的上下文中运行。

线程共享相同的虚拟地址空间、内存(无论好坏)、一组打开的文件描述符等等。

流程(本质上)彼此完全分离,并且不能互相修改。

你应该阅读这个问题:


举个例子,如果我是你的 shell(例如bash),当你输入一个类似的命令时ls,我将进入fork()一个新进程,然后exec()ls可执行文件。(然后我wait()在子进程上,但这超出了范围。)这发生在完全不同的地址空间中,如果ls爆炸,我不在乎,因为我仍在自己的进程中执行。

另一方面,假设我是一个数学程序,我被要求将两个 100x100 矩阵相乘。我们知道矩阵乘法是一个令人尴尬的并行问题。所以,我有记忆中的矩阵。我产生了 N 个线程,每个线程都对相同的源矩阵进行操作,将它们的结果放在结果矩阵中的适当位置。请记住,它们是在同一个过程的上下文中运行的,所以我需要确保它们不会在彼此的数据上打上烙印。如果 N 为 8 并且我有一个八核 CPU,我可以有效地同时计算矩阵的每个部分。

于 2012-07-26T05:22:15.353 回答
0

使用 fork() (和家族)在 unix 上的进程创建机制非常有效。此外,大多数 unix 系统不支持内核级线程,即线程不是内核识别的实体。因此,此类系统上的线程无法从内核级别的 CPU 调度中受益。pthread 库所做的不是 kerenl 而是某些进程本身。同样在这样的系统上,pthreads 是使用 vfork() 实现的,并且仅作为轻量级进程。因此,除了在此类系统上的可移植性外,使用线程没有任何意义。

据我了解,Sun-solaris 和 windows 具有内核级线程,而 linux 系列不支持内核线程。

使用进程管道和 unix 域套接字是非常有效的 IPC,没有同步问题。我希望它清楚为什么以及何时应该实际使用线程。

于 2013-10-09T04:17:48.453 回答