clone()和fork比pthreads好吗?

时间:2012-07-26 05:17:27

标签: linux pthreads clone fork

我是这个领域的初学者。

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

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

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

先谢谢。

4 个答案:

答案 0 :(得分:30)

clone(2)是特定于Linux的syscall,主要用于实现线程(特别是,它用于pthread_create)。通过各种参数,clone也可以具有类似fork(2)的行为。很少有人直接使用clone,使用pthread库更具可移植性。您可能只需要在实现自己的线程库(Posix线程的竞争对手)时直接调用clone(2)系统调用,而这非常棘手(特别是因为锁定可能需要使用{ {3}}系统调用程序集编码例程中的系统调用,请参阅futex(2))。您不希望直接使用clonefutex,因为pthreads使用起来要简单得多。

(其他pthread函数需要在libpthread.so期间clone之后pthread_create内部完成一些簿记工作

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

在类Unix系统上,所有进程(pid 1的第一个进程,通常为init除外)由fork(或vfork之类的变体创建1}};您可以(但不想)以clone的行为方式使用fork

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

forkexecve系统调用是Unix进程创建的核心(使用open和相关的系统调用)。

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

我建议你阅读一本好的Unix编程书,如pthread tutorial和/或(免费提供的)Advanced Unix Programming

答案 1 :(得分:14)

fork(和公司)的优点和缺点是他们创建了一个新流程,它是现有流程的克隆。

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

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

换句话说,使用线程有点赌博。只要您的代码完全干净,您就可以通过在单个进程中使用多个线程来获得一些效率。使用多个进程会增加一些开销,但可以使您的代码更加健壮,因为它可以限制单个问题可能造成的损害,并且如果它遇到一个主要问题,可以很容易地关闭和替换进程问题

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

答案 2 :(得分:5)

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

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

进程(基本上)彼此完全分离,不能相互修改。

你应该读这个问题:


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

另一方面,说我是数学课程,我被要求将两个100x100矩阵相乘。我们知道矩阵乘法是一个Embarrassingly Parallel问题。所以,我有记忆中的矩阵。我产生了N个线程,每个线程在相同的源矩阵上运行,将它们的结果放在结果矩阵中的适当位置。请记住,这些操作是在同一个过程的上下文中进行的,因此我需要确保它们不会相互标记彼此的数据。如果N是8并且我有一个八核CPU,我可以同时有效地计算矩阵的每个部分。

答案 3 :(得分:0)

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

根据我的理解,Sun-solaris和windows有内核级线程,linux系列不支持内核线程。

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