在多線程程序中fork出一個新進程,發現新的進程沒法正常工做。由於:在使用fork時會將原來進程中的全部內存數據複製一份保存在子進程中。可是在拷貝的時候,可是線程是沒法被拷貝的。若是在原來線程中加了鎖,在使用的時候會形成死鎖。能夠將開線程的代碼放在fork之後。也就是放在新的子進程中進行建立。promise
在多線程程序裏,在」自身之外的線程存在的狀態」下一使用fork的話,就可能引發各類各樣的問題.比較典型的例子就是,fork出來的子進程可能會死鎖.請不要,在不能把握問題的原委的狀況下就在多線程程序裏fork子進程.
在子進程的執行開始處調用doit()時,發生死鎖的機率會很高.安全
void* doit(void*) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&mutex); struct timespec ts = {10, 0}; nanosleep(&ts, 0); // // 睡10秒 pthread_mutex_unlock(&mutex); return 0; } int main(void) { pthread_t t; pthread_create(&t, 0, doit, 0); // 作成並啓動子線程 if (fork() == 0) { //子進程 //在子進程被建立的瞬間,父的子進程在執行nanosleep的場合比較多 doit(0); return 0; } pthread_join(t, 0); // 等待子線程結束 }
如下是說明死鎖的理由:
通常的,fork作以下事情數據結構
在內存區域裏,靜態變量mutex的內存會被拷貝到子進程裏.並且,父進程裏即便存在多個線程,但它們也不會被繼承到子進程裏. fork的這兩個特徵就是形成死鎖的緣由.多線程
以上程序執行流程異步
像這裏的doit函數那樣的,在多線程裏由於fork而引發問題的函數,咱們把它叫 作」fork-unsafe函數」.反之,不能引發問題的函數叫作」fork-safe函數」.雖然在一些商用的UNIX裏,源於OS提供的函數(系統調 用),在文檔裏有fork-safety的記載,可是在 Linux(glibc)裏固然!不會被記載.即便在POSIX裏也沒有特別的規定,因此那些函數是fork-safe的,幾乎不能判別.不明白的話,做 爲unsafe考慮的話會比較好一點吧.(2004/9/12追記)Wolfram Gloger說過,調用異步信號安全函數是規格標準,因此試着調查了一下,在pthread_atforkの這個地方里有」 In the meantime*5, only a short list of async-signal-safe library routines are promised to be available.」這樣的話.好像就是這樣.async
malloc函數就是一個維持自身固有mutex的典型例子,一般狀況下它是fork-unsafe的.依賴於malloc函數的函數有不少,例如printf函數等,也是變成fork-unsafe的.函數
直目前爲止,已經寫上了thread+fork是危險的,可是有一個特例須要告訴你們.」fork後立刻調用exec的場合,是做爲一個特列不會產生問題的」. 什麼緣由呢..?exec函數*6一被調用,進程的」內存數據」就被臨時重置成很是漂亮的狀態.所以,即便在多線程狀態的進程裏,fork後不立刻調用一切危險的函數,只是調用exec函數的話,子進程將不會產生任何的誤動做.可是,請注意這裏使用的」立刻」這個詞.即便exec前僅僅只是調用一回printf(「I’m child process」),也會有死鎖的危險.
譯者注:exec函數裏指明的命令一被執行,該命令的內存映像就會覆蓋父進程的內存空間.因此,父進程裏的任何數據將不復存在.spa
本blog的理解:查看前面進程建立中,子進程在建立後,是寫時複製的,也就是子進程剛建立時,與父進程同樣的副本,當exce後,那麼老的地址空間被丟棄,而被新的exec的命令的內存的印像覆蓋了進程的內存空間,因此鎖的狀態可有可無了。
規避方法1:作fork的時候,在它以前讓其餘的線程徹底終止.
在fork以前,讓其餘的線程徹底終止的話,則不會引發問題.但這僅僅是可能的狀況.還有,由於一些緣由而其餘線程不能結束就執行了fork的時候,就會是產生出一些解析困難的不具合的問題.
規避方法2:fork後在子進程中立刻調用exec函數
不用使用規避方法1的時候,在fork後不調用任何函數(printf等)就立刻調用execl等,exec系列的函數.若是在程序裏不使用」沒有exec就fork」的話,這應該就是實際的規避方法吧.把本來子進程應該作的事情寫成一個單獨的程序,編譯成可執行程序後由exec函數來調用.線程
規避方法3:」其餘線程」中,不作fork-unsafe的處理
除了調用fork的線程,其餘的全部線程不要作 fork-unsafe的處理.爲了提升數值計算的速度而使用線程的場合*7,這多是fork- safe的處理,可是在通常的應用程序裏則不是這樣的.即便僅僅是把握了那些函數是fork-safe的,作起來還不是很容易的.fork-safe函 數,必須是異步信號安全函數,而他們都是能數的過來的.所以,malloc/new,printf這些函數是不能使用的.調試
規避方法4:使用pthread_atfork函數,在即將fork以前調用事先準備的回調函數.apue中詳細介紹了它
使用pthread_atfork函數,在即將 fork以前調用事先準備的回調函數,在這個回調函數內,協商清除進程的內存數據.可是關於OS提供的函數 (例:malloc),在回調函數裏沒有清除它的方法.由於malloc裏使用的數據結構在外部是看不見的.所以,pthread_atfork函數幾乎 是沒有什麼實用價值的.
規避方法5:在多線程程序裏,不使用fork
就是不使用fork的方法.即用pthread_create來代替fork.這跟規避策2同樣都是比較實際的方法,值得推薦.
總結:在程序正常運行時出現不能正常工做,而在調試時又能正常工做。則能夠考慮死鎖的狀況!