clone()和fork比pthreads好吗?

Bri*_*era 13 linux fork clone pthreads

我是这个领域的初学者.

我研究过fork(),vfork(),clone()和并行线程.

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

但是,当是fork()clone()比并行线程更好?你能通过给出现实世界的例子向我解释一下吗?

提前致谢.

Bas*_*tch 32

clone(2)是一个Linux特定的系统调用,主要用于实现线程(特别是,它用于pthread_create).有了各种参数,clone也可以有类似fork(2)的行为.很少有人直接使用clone,使用pthread库更加便携.你可能只需要clone(2)在实现自己的线程库(Posix线程的竞争对手)时直接调用syscall,这非常棘手(特别是因为锁定可能需要在机器调整的汇编编码例程中使用futex(2)系统调用,见futex(7)).您不希望直接使用clonefutex因为pthreads更易于使用.

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

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

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

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

forkexecve系统调用是中央的Unix进程创造(与waitpid函数和相关的系统调用).

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

我建议你阅读一本优秀的Unix编程书,如高级Unix编程和/或(免费提供的)高级Linux编程


Jer*_*fin 14

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

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

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

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

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

  • @smoku:没有人说这是个问题.但是,您说创建进程比创建线程有更多的开销是一个神话.如果你想改变你的位置,声称开销通常是完全合理的,我完全同意 - 但声称复制兆字节的数据不会产生任何开销,相比之下*不*复制那些数兆字节的数据根本就是一个彻头彻尾的谬误. (3认同)
  • 创建进程比创建线程更具开销是一个神话.在Linux上,两者都是内部的内核任务,以相同的开销创建(感谢copy-on-write"magic").使用共享资源进行通信比使用消息传递IPC更有效也是一个神话 - 特别是在现代多核机器上,通信线程可能驻留在不同的CPU芯片上. (2认同)
  • @smoku:不,这不是神话。即使使用 COW,创建新进程也需要复制现有进程的页表,而创建线程只是重新使用现有的页表。是的,它将需要复制的内存量减少了几个数量级,但是在使用大量内存的进程上,页表仍然很容易达到几兆字节。 (2认同)

Jon*_*art 5

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

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

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

你应该读这个问题:


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

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