当多线程进程调用fork创建子进程时,Pthreads指定只有那个调用fork的线程在子进程内存在(表示子进程中只有调用线程这个线程)。尽管当从fork调用返回时,只有调用线程在子进程中存在,所有其他的Pthreads线程状态仍保留为与调用fork时相同的状态。在子进程中,线程拥有与在父进程内相同的状态。它拥有相同的互斥量,同样的线程私有数据键值等。尽管当调用fork时在同步对象上等待的任何线程不再等待,所有的互斥量和条件变量仍然存在(因为其他线程不在子进程存在,所以他们怎么能等待呢?)。
注:fork调用不会影响互斥量的状态。如果它在父进程中被锁住,则它在子进程中被锁!
如果一个互斥量在fork调用时被锁,则它在子进程中仍然被锁。因为一个加锁的互斥量被锁住它的线程拥有,只有锁住互斥量的线程是调用fork的那个线程时,互斥量可以在子进程中被开锁。这是重要的如果当你调用fork时,另外的线程把一个互斥量锁住,则你将失去对该互斥量和由该互斥量控制的任何数据的存取。
因为没有调用线程私有数据销毁和清除处理函数,你可能需要担心存储泄漏问题。
1.fork处理器
[cpp]
int pthread_atfork(void (*prepare)(void),void (*parent)(void),void(*child)(void));
int pthread_atfork(void (*prepare)(void),void (*parent)(void),void(*child)(void)); Pthreads增加了pthread_atfork ”fork处理器”机制以允许你的代码越过fork调用保护数据和不变量。这与atexit有点类似,后者在一个进程终止时允许程序执行清除操作。使用pthread_atfork,你需要提供三个独立的处理函数地址。prepare fork处理器在父进程调用fork之前调用,parent fork处理器在fork执行后在父进程内被调用,child fork处理器在fork执行后在子进程内被调用。
通常,pthread fork处理器以正确的顺序锁住所有的由相关代码,使用的互斥量以阻止死锁的发生。调用fork的线程将在prepare fork处理器中阻塞直到它锁住了所有的互斥量后,这就保证了其他线程不能锁住某个互斥量或修改子进程可能需要的数据。parent fork处理器只需要开锁所有互斥量即可,以允许父进程和所有线程继续正常工作。
child fork处理器经常可以与parent fork处理器一样;但是有时需要重置程序或库的状态。例如:如果使用daemon线程在后台执行函数,你或者需要记录那些线程不再存在的事实,或者在子进程内创建新线程来执行同样的函数。你可能需要重置计数器,释放堆存储等。
[cpp]
/*
* atfork.c
*
* Demonstrate the use of "fork handlers" to protect data
* invariants across a fork.
*/
#include
#include
#include
#include "errors.h"
pid_t self_pid; /* pid of current process */
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/*
* This routine will be called prior to executing the fork,
* within the parent process.
*/
void fork_prepare (void)
{
int status;
/*
* Lock the mutex in the parent before creating the child,
* to ensure that no other thread can lock it (or change any
* associated shared state) until after the fork completes.
*/
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock in prepare handler");
printf("fork_prepare\n");
}
/*
* This routine will be called after executing the fork, within
* the parent process
*/
void fork_parent (void)
{
int status;
/*
* Unlock the mutex in the parent after the child has been created.
*/
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock in parent handler");
printf("fork_parent\n");
}
/*
* This routine will be called after executing the fork, within
* the child process
*/
void fork_child (void)
{
int status;
/*
* Update the file scope "self_pid" within the child process, and unlock
* the mutex.
*/
self_pid = getpid ();
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock in child handler");
printf("fork_child: self_pid = %d\n",self_pid);
}
/*
* Thread start routine, which will fork a new child process.
*/
void *thread_routine (void *arg)
{
pid_t child_pid;
int status;
child_pid = fork ();
if (child_pid == (pid_t)-1)
errno_abort ("Fork");
/*
* Lock the mutex -- without the atfork handlers, the mutex will remain
* locked in the child process and this lock attempt will hang (or fail
* with EDEADLK) in the child.
*/
status = pthread_mutex_lock (&mutex);
if (status