線程和fork
對於fork相信大家都已經熟悉的不能再熟悉了! 我們最近學習到了線程,我們都知道了新創建的線程可以訪問進程的地址空間,並且
繼承調用線程的浮點環境和信號屏蔽字.線程中都可以訪問到進程的分別有: 1.文件描述符表 2.每種信號的處理方式 3.當前工作目
錄 4.用戶id和組id 但是線程的有些資源是每個線程獨有的: 1.線程id 2.上下文,包括各種寄存器的值,程序計數器和棧指針.
3.棧空間 4.errno變量 5.信號屏蔽字6.調度優先級 那麼我們來思考一下! 在一個線程中調用fork,會發生什麼情況呢 ?
當線程調用fork的時候,就是爲了子進程創建了整個進程地址空間的副本,子進程與父進程是兩個完全不同的進程,只要兩者都沒有
對內存做出改動,父進程和子進程之間可以共享內存頁的副本.
子進程通過繼承整個地址空間的副本,還從父進程哪裏繼承了每一個互斥量,讀寫鎖和條件變量的狀態. 如果父進程包含一個以上的
線程,子進程在fork返回以後,如果緊接着不是馬上調用exec的話,就需要清理鎖的狀態,
在子進程的內部,只存在一個線程,它是由父進程中調用fork的線程的副本構成的,如果父進程中的線程佔有鎖,子進程將同樣佔有
這些鎖. 問題是子進程並不包含佔有鎖的線程的副本,所以子進程沒有辦法知道它佔有了那些鎖,需要釋放那些鎖.
如果子進程從fork返回以後馬上調用一個exec函數就可以避免這樣的問題! 這種情況下,舊的地址空間就會被丟棄,所以鎖的狀態無
關緊要,但如果子進程進行做處理工作的時候,這種策略就行不用了. 這個時候系統提供了一個pthread_atfork函數送給你.
#include <pthread.h>
int pthread_atfork(void (*prepare)(void), void (*parent)(void),void (*child)(void));
用pthread_atfork函數最多可以安裝三個幫助清理鎖的函數. prepare fork處理程序有父進程在fork創建子進程之前調用,這個fork
處理程序的任務是獲取父進程定義的所有鎖 parent fork 處理程序是在fork創建子進程之後,返回之前在父進程上下文之中調用的.
這個fork處理程序的任務是對prepare fork處理程序獲取的所有鎖進行解鎖. child fork處理程序在fork函數之前在子進程上下文
之中調用. 與parent fork處理程序一樣,child fork處理程序也必須釋放prepare fork處理程序獲取的所有鎖.
注意,不會出現加鎖一次解鎖兩次的情況,雖然看起來也許會出現. 子進程地址空間在創建時就得到了父進程的所有鎖的副本. 因爲
prepare fork處理程序獲取了所有的鎖,父進程中的內存和子進程中的內存內容在開始的時候是相同的. 當父進程和子進程對他們鎖
的副本進程解鎖的時候,新的內存是分配給子進程的,父進程的內存內容是賦值到子進程的內存中(寫時拷貝),所以我們就會陷入這
種假象,看起來父進程對它的所有鎖的副本進行加鎖,子進程對它的所有鎖的副本進行了加鎖,父進程和子進程對不同單元的重複的
鎖都進行了解鎖操作,就好像出現了下列的事件:
注:fork後的父子進程鎖的狀態
(1)父進程獲取所有的鎖
(2)子進程獲取所有鎖
(3)父進程釋放它的鎖
(4)子進程釋放它的鎖
從而可以多次調用pthread_atfork函數從而設置多套fork處理程序. 如果不需要使用其中某個處理程序,可以給特定的處理程序參數
傳入空指針,它就不會起任何作用.使用多個fork處理程序時,處理程序的調用順序並不相同. parent和child fork處理程序是以
它們註冊時的順序進行調用的,而prepare fork處理程序的調用順序與它們註冊時的順序相反. 這樣就可以允許多個模塊註冊它們
自己的fork處理程序,並且可以保持鎖的層次.
加入模塊A調用模塊B當中的函數,並且每個模塊有自己的一套鎖. 如果鎖的層次是A在B之前,模塊B必須在模塊A之前設置它的fork處
理程序. 當父進程調用fork時,就會執行下列步驟:
(1)調用模塊A的prepare fork處理程序獲取模塊A的所有鎖
(2)調用模塊B的prepare fork處理程序獲取模塊B的所有鎖
(3)創建子進程
(4)調用模塊B中的child fork處理程序獲取子進程中的模塊B的所有鎖
(5)調用模塊A中的child fork處理程序釋放子進程當中模塊A的所有鎖
(6)fork函數返回到子進程.
(7)調用模塊B中的parent fork處理程序釋放父進程中的模塊B的所有鎖
(8)調用模塊A中的parent fork處理程序來釋放父進程中模塊A的所有鎖.
(9)fork函數返回到父進程.
如果fork處理程序是用來清理鎖的狀態的,那麼又由誰來負責清理條件變量的狀態呢? 在有些操作系統的實現中,條件變量可能並
不需要做任何清理. 但是有些操作系統實現把鎖作爲條件變量實現的一部分,這種情況下的條件變量就需要清理. 問題是目前不存
在允許清理鎖狀態的接口.如果所示嵌入到條件變量的數據結構中的,那麼在調用fork之後就不能夠使用條件變量了,因爲還沒有可移
植的方法對鎖進行狀態清理. 另外,如果操作系統的實現是使用全局鎖保護進程中所有的條件變量數據結構,那麼操作系統實現本身
可以在fork庫例程中做清理鎖的工作,但是應用程序不應該依賴操作系統實現中類似這樣的細節.