1 线程中调用fork()产生的问题
线程可以通过调用fork()来创建新的进程,新的进程只有一个线程,它是调用fork()的线程的副本。由于写时拷贝(创建新的进程时,子进程共享父进程的地址空间,只有父进程或者子进程进行写操作时,才会拷贝父进程的东西来创建新的子进程的地址空间)的作用,此时的子进程中的线程共享父进程的调用fork()的线程的地址空间,如果父进程或者子进程对内存做了改动,就会创建父进程的副本,此时,子进程就从父进程那么继承了所有互斥量、读写锁和条件变量的状态。但是,如果父进程在调用fork()之前就获得了某些锁,而子进程获得的状态是锁定的,它就没有办法知道是否该释放哪些锁。换句话说,子进程中的锁是被不存在的线程锁定的。因此,该锁永远不会被释放,
2 解决方案
通常建议,在调用fork()后,子进程立即调用exec()来重置子进程的所有状态。
但是,这并没有从根本上解决问题。应用程序并不知道关于锁的任何问题,他们可能会在fork()和exec()之间调用其它的函数,那么,原来的问题还是来了。
为了解决这个问题,poxis多线程库提供了pthread_atfork()函数。
pthread_atfork()为多线程提供了一种保护子进程的机制,允许程序清理锁的状态。
int pthread_atfork(void (*prepare)(), void (*parent)(), void (*child)());
从它的原型可以看到,它的参数是三个函数:prepare, parent, child。他们分别在三个时期进行调用。
prepare由父进程在fork()创建子进程前调用,负责获取父进程的所有锁。
parent在fork()创建子进程之后,返回子进程前在父进程环境中调用,负责释放prepare获得的所有锁。
child在fork()返回之前,在子进程环境中调用,负责释放prepare获得的所有锁。