今天在看《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處理常式是不是一樣會阻塞?
大概到這裡為止,以後如果想到什麼再更新~~