在 POSIX 标准中,fork 的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略,所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。所有父进程中别的线程,到了子进程中都是突然蒸发掉的。
在bionic pthread_mutex_lock 和pthread_mutex_unlock时,如果用RECURSIVE锁,去lock内部的另一个锁,_recursive_lock,来保证RECURSIVE锁内部逻辑。代码见:android4.0.4/bionic/libc/bionic/pthread.c
fork的逻辑,先执行__bionic_atfork_run_prepare获得一个handler_mutex的RECURSIVE锁,然后再__fork()之后,子进程和父进程分别执行__bionic_atfork_run_parent和__bionic_atfork_run_child来释放这个锁。
代码见:
android4.0.4/bionic/libc/bionic/pthreadatfork.c和android4.0.4/bionic/libc/bionic/fork.c
这样假设一种情况:
父进程有两个线程A和B,线程A去执行fork,先执行__bionic_atfork_run_prepare,获取handler_mutex,然后执行__fork()来产生一个子进程,就在fork的一瞬间
,B线程正好用另一个RECURSIVE锁,进入pthread_mutex_lock或pthread_mutex_unlock,获取_recursive_lock,还没释放,A线程__fork()来产生一个子进程,复制了父进程的所有对象(包括_recursive_lock的状态),而丢掉了B线程。这样_recursive_lock在子进程里永远没人释放。然后子进程试图去执行__bionic_atfork_run_child,执行pthread_mutex_unlock(&handler_mutex),再回获取_recursive_lock时,卡死。
这个bug是POSIX标准里,多线程fork bug的一种体现,详细信息可以参照:http://www.linuxprogrammingblog.com/threads-and-fork-think-twice-before-using-them