我是这个领域的初学者。
我研究过fork()
,vfork()
,clone()
和pthreads。
我注意到pthread_create()
将创建一个线程,这比使用fork()
创建新进程的开销更小。此外,该线程将与父进程共享文件描述符,内存等。
但是什么时候fork()
和clone()
比pthreads更好?你能通过给出现实世界的例子向我解释一下吗?
先谢谢。
答案 0 :(得分:30)
clone(2)是特定于Linux的syscall,主要用于实现线程(特别是,它用于pthread_create
)。通过各种参数,clone
也可以具有类似fork(2)的行为。很少有人直接使用clone
,使用pthread库更具可移植性。您可能只需要在实现自己的线程库(Posix线程的竞争对手)时直接调用clone(2)
系统调用,而这非常棘手(特别是因为锁定可能需要使用{ {3}}系统调用程序集编码例程中的系统调用,请参阅futex(2))。您不希望直接使用clone
或futex
,因为pthreads使用起来要简单得多。
(其他pthread函数需要在libpthread.so
期间clone
之后pthread_create
内部完成一些簿记工作
当futex(7)回答时,进程有自己的地址空间和文件描述符集。并且进程可以使用Jonathon系统调用执行新的可执行程序,它基本上初始化地址空间,堆栈和寄存器以启动新程序(但可以保留文件描述符,除非使用 close-on -exec 标志,例如execve的O_CLOEXEC
。
在类Unix系统上,所有进程(pid 1的第一个进程,通常为init
除外)由fork
(或vfork
之类的变体创建1}};您可以(但不想)以clone
的行为方式使用fork
。
(从技术上讲,在Linux上,有一些奇怪的异常可以忽略,特别是内核进程或线程以及一些罕见的内核启动的进程启动,如/sbin/hotplug
....)
fork
和execve
系统调用是Unix进程创建的核心(使用open和相关的系统调用)。
多线程进程有多个线程(通常由pthread_create
创建),它们共享相同的地址空间和文件描述符。当您希望在同一地址空间内的相同数据上并行工作时,可以使用线程,但是您应该关心同步和锁定。阅读waitpid了解更多信息。
我建议你阅读一本好的Unix编程书,如pthread tutorial和/或(免费提供的)Advanced Unix Programming
答案 1 :(得分:14)
fork
(和公司)的优点和缺点是他们创建了一个新流程,它是现有流程的克隆。
这是一个弱点,因为正如您所指出的,创建新流程会产生相当大的开销。它还意味着进程之间的通信必须通过一些“批准的”通道(管道,套接字,文件,共享内存区域等)完成。
这是一种力量,因为它在父母和孩子之间提供了(更多)更大的隔离。例如,如果子进程崩溃,您可以杀死它并相当容易地启动另一个进程。相比之下,如果一个子线程死了,杀死它最多也是有问题的 - 不可能确定该线程专有的资源,所以你不能在它之后进行清理。同样,由于进程中的所有线程共享一个公共地址空间,一个遇到问题的线程可能会覆盖所有其他线程正在使用的数据,因此只杀死一个线程不一定足以清理混乱
换句话说,使用线程有点赌博。只要您的代码完全干净,您就可以通过在单个进程中使用多个线程来获得一些效率。使用多个进程会增加一些开销,但可以使您的代码更加健壮,因为它可以限制单个问题可能造成的损害,并且如果它遇到一个主要问题,可以很容易地关闭和替换进程问题
就具体的例子而言,Apache可能是一个非常好的例子。它将在每个进程中使用多个线程,但是为了在出现问题时限制损坏(除其他外),它会限制每个进程的线程数,并且可以/将产生同时运行的多个单独进程。例如,在一个体面的服务器上,您可能拥有8个进程,每个进程有8个线程。大量线程帮助它在大多数I / O绑定任务中为大量客户端提供服务,并将其分解为进程意味着如果出现问题,它不会突然变得完全无响应,并且可以关闭并重新启动一个过程而不会丢失很多。
答案 2 :(得分:5)
这些是完全不同的东西。 fork()
创建了一个新的process。 pthread_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,没有同步问题。 我希望它能清除为什么以及何时应该使用线程。