当线程调用 fork 时,就为子进程创建了整个进程地址空间的副本。子进程通过继承整个地址空间的副本,也从父进程那里继承了所有互斥量、读写锁和条件变量的状态。如果父进程包含多个进程,子进程在 fork 返回之后,如果紧接着不是马上调用 exec 的话,就需要清理锁状态。
要清除锁状态,可以通过调用 pthread_atfork 函数建立 fork 处理程序。
#include <pthread.h>
int pthread_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void));
父进程和子进程对在不同内存位置的重复的锁都进行了解锁操作,就好像出现了下列的事件序列:
1、父进程获得所有的锁
2、子进程获得所有的锁
3、父进程释放它的锁
4、子进程释放它的锁
下面是例程:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
void prepare(void)
{
printf("preparing locks...\n");
pthread_mutex_lock(&lock1);
pthread_mutex_lock(&lock2);
}
void parent(void)
{
printf("parent unlocking locks...\n");
pthread_mutex_unlock(&lock1);
pthread_mutex_unlock(&lock2);
}
void child(void)
{
printf("child unlocking locks...\n");
pthread_mutex_unlock(&lock1);
pthread_mutex_unlock(&lock2);
}
void *thr_fn(void *arg)
{
printf("thread started...\n");
pause();
return 0;
}
int main()
{
int err;
pid_t pid;
pthread_t tid;
#if defined(BSD) || defined(MACOS)
printf("pthread_atfork is unsupported\n");
#else
if ((err = pthread_atfork(prepare, parent, child)) != 0) {
printf("can't install fork handlers: %s\n", strerror(err));
exit(1);
}
err = pthread_create(&tid, 0, thr_fn, 0);
if (err != 0) {
printf("can't create thread: %s\n", strerror(err));
exit(1);
}
sleep(2);
printf("parent about to fork...\n");
if ((pid = fork()) < 0) {
printf("fork failed\n");
exit(1);
} else if (pid == 0)
printf("child returned from fork\n");
else
printf("parent returned from fork\n");
#endif // defined
exit(0);
}
程序中定义了两个互斥量,lock1 和 lock2,prepare fork 处理程序获取这两把锁,child fork 处理程序在子进程环境中释放锁,parent fork 处理程序在父进程中释放锁。
运行该程序得到如下输出:
可以看出,parent fork 处理程序在调用 fork 以后运行,child fork 处理程序在 fork 调用返回到子进程之前运行,parent fork 处理程序在 fork 调用返回给父进程前运行。