今天在看《Unix环境高级编程》的时候卡在这一章节很久,在这里有比较多不理解的地方,于是想写篇blog来整理一下。
在“线程和fork ”这一章节中提到了这样一个问题:“在子进程内部只存在一个线程,它是由父进程中调用fork的线程的副本构成的。如果父进程中的线程占有锁,子进程同样占有这些锁。问题是子进程并不包含占有锁的线程的副本,所以子进程没有办法知道它占有哪些锁并且需要释放哪些锁。”。书中提到了可以调用pthread_atfork函数建立fork处理程序来清除锁的状态。
在这里不太理解这个函数所引出的机制。首先引用一下书上的一段话:“注意不会出现加锁一次解锁两次的情况,虽然看起来也许会出现。当子进程地址空间创建的的时候,它得到了所有锁的副本。因为prepare fork处理获取所有的锁,父进程中的内存和子进程中内存内容在开始的时候是相同的,当父进程和子进程对他们的锁的副本进解锁的时候,新的内存是分配给子进程的,父进程的内存内容被复制到子进程的内存中(写时复制),所以就会陷入这样的假象,看起来父进程对它所有的锁的副本进行了加锁,子进程对它所有的锁的副本进行了加锁。父进程和子进程对在不同内存位置的重复的锁都进行了解锁操作,就好像出现下列的事件序列:
1、父进程获得所有的锁。
2、子进程获得所有的锁。
3、父进程释放它的锁。
4、子进程释放它的锁。
--------------------------------------------------------------------------------------------------------分割线--------------------------------------------------------------------------------------------------------------------------------------------------
在这里算是有比较不太理解,或者说不清楚的地方。于是结合书上的示例程序,我自己对书上的那段话所引出的机制做出了的一个猜想,用我自己的话总结起来就是:
在调用了pthread_atfork函数建立了3个帮助清理锁的函数的情况下,父进程的一个线程调用fork时,首先会运行prepare fork处理程序来让父进程占有所有的锁,在运行prepare fork处理程序之后,父进程所有的锁都处于上锁状态。在此之后运行child fork处理程序,在运行child fork时是子进程已经创建,但fork未返回。此时子进程的地址空间内有继承于父进程所有处于上锁状态的锁,因为父进程和子进程上的内存在开始的时候是相同的。那么当运行child fork处理程序对子进程继承而来的锁进行解锁时,将会触发写时复制机制,从而使子进程获得一份属于自己的内存空间,并用这块内存空间来管理自己的锁。最后在fork返回后,parent fork处理程序运行,父进程解锁所有获得的所有处于上锁状态的锁,父进程原先的多个线程继续利用各个锁正常工作。
再简单些总结就是,因为在父进程的一个线程fork之后,子进程没有办法知道它占有哪些锁并且需要释放哪些锁。所以,父进程可以先将所有锁设置为上锁状态,再让子进程来继承,这样就让子进程知道,它获得的所有锁都是已经占有的,它想再利用这些锁的时候,只需要运行child fork来为这些已经上锁的锁解锁则可。同样道理,父进程在让子进程继承了自己已经上锁的锁之后,只需要运行parent fork来为这些锁解锁,则可以让自己的多个线程继续利用这些锁工作。(自己YY出来的,不知道正确与否,而且自己还木有装Linux,所以完全木有试验过~~)
不过,自己还有些疑问,大概如下:
1、为什么书上写父进程的锁的时候都是用副本来形容?例如:“当父进程和子进程对他们的锁的副本进行解释时....”,父进程用的不就是原先那些锁吗?为什么是副本?
2、在运行prepare fork处理程序时,想要获得所有的锁,是不是也要等到父进程的线程将锁释放之后才能拥有这些锁?
如果是这样的话,preapare fork处理程序是不是一样会阻塞?
大概到这里为止,以后如果想到什么再更新~~