进程复制和终止

因为所有进程至少有一个线程,所以创建(也就是复制)和终止进程意味着线程的创建和终止。

此部分描述了复制和终止进程时线程和进程之间的交互作用。

要了解更多有关进程复制和终止的信息请阅读以下内容:

分叉

在下列情况下,程序员调用 fork 子例程:

  • 要在同一程序内创建新的控制流。 AIX创建一个新进程。
  • 要创建一个运行不同程序的新进程。 在这种情况下,对 fork 子例程的调用很快将后跟对其中一个 exec 子例程的调用。

在多线程程序中,第一次使用 fork 子例程 (创建新的控制流) 由 pthread_create 子例程提供。 因此 fork 子例程应该只被用于运行新程序。

fork 子例程复制父进程,但只复制调用线程;子进程为单线程进程。 父进程的调用线程变成了子进程的初始线程;它可能不是父进程的初始线程。 因此,如果子进程的初始线程从其入口点例程返回,那么该子进程将终止。

在复制父进程时,fork 子例程还复制所有的同步变量,包括其状态。 因此,例如互斥对象可能被子进程中不再存在的线程挂起,且任何相关的资源有可能不一致。

强烈建议只将 fork 子例程用于运行新程序,并在子进程中调用 fork 子进程后尽可能快地调用 exec 子例程之一。

派生处理程序

前述的派生规则没有说明需要多线程库。 应用程序可能不知道多线程库正在使用中,从而将调用 forkexec 子例程之间任意数量的库例程,就像它们始终做的一样。 实际上,它们可能是旧的单线程程序,因此不能期望它们遵循该线程库所强制的新限制。

另一方面,派生期间万一子进程中有例程稍后被调用,多线程库需要使用一种方式来保护其内部状态。 该问题在多线程输入/输出库中特别容易出现,多线程输入/输出库在 forkexec 子例程之间几乎肯定会被调用,以影响输入/输出重定向。

pthread_atfork 子例程为多线程库提供了一种方法来保护自己免受调用 fork 子例程的无辜应用程序的伤害。 它还为多线程应用程序提供一种标准机制,保护它们不会调用库例程中的 fork 子例程或是应用程序本身。

在调用 fork 子例程之前和之后,pthread_atfork 子例程将派生处理程序注册为要调用。 该派生处理程序在称为 fork 子例程的线程中被执行。 存在以下派生处理程序:
子例程 描述
做好准备 该 prepare 派生处理程序在开始处理 fork 子例程之前被调用。
父代 该 parent 派生处理程序在父进程中的 fork 子例程完成处理之后被立即调用。
子代 该 child 派生处理程序在子进程中的 fork 子例程完成处理之后被立即调用。

进程终止

prepare 派生处理程序以后进先出 (LIFO) 的顺序被调用,而 parent 和 child 派生处理程序以先进先出 (FIFO) 的顺序被调用。 这允许程序保存任何期望的锁定顺序。

当进程终止时,通过显式或隐式地调用 exitatexit_exit 子例程,将终止进程中的所有线程。 既不会调用清除处理程序也不会调用特定于线程的数据析构函数。
注: unatexit 子例程将注销先前由 atexit 子例程注册的函数。 如果找到了引用的函数,它将从在程序正常中止时所调用的函数的列表中被除去。

这种行为的原因是没有要清除的状态,也没有要回收的特定于线程的存储空间,因为整个进程,包括所有的线程都终止了,且回收了所有进程的存储空间,包括所有特定于线程的存储空间。