通常的,fork作以下事情html
在內存區域裏,靜態變量 mutex 的內存會被拷貝到子進程裏。並且,父進程裏即便存在多個線程,但它們也不會被繼承到子進程裏。fork 的這兩個特徵就是形成死鎖的緣由。多線程
#include <stdio.h> #include <unistd.h> #include <time.h> #include <pthread.h> void *doit(void *arg) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&mutex); fprintf(stderr, "lock... pid = %d\n", getpid()); /* 線程持有鎖,進入睡眠 */ struct timespec tc = {10, 0}; nanosleep(&tc, 0); fprintf(stderr, "unlock... pid = %d\n", getpid()); pthread_mutex_unlock(&mutex); return NULL; } int main(int argc, char **argv) { pid_t pid; pthread_t t; struct timespec tc = {2, 0}; pthread_create(&t, 0, doit, 0); // sleep,讓線程執行 doit() 並佔有鎖 nanosleep(&tc, 0); if ((pid = fork()) == 0) { /* 在線程持有鎖期間進行fork,在子進程中,線程將會佔有鎖並死去 */ fprintf(stderr, "[child] fork...\n"); /* 再次試圖加鎖,此時進入死鎖 */ doit(NULL); return 0; } fprintf(stderr, "[parent] child pid = %d\n", pid); pthread_join(t, 0); return 0; }
死鎖緣由的詳細解釋 ---函數
像這裏的 doit() 函數那樣的,在多線程裏由於 fork 而引發問題的函數,咱們把它叫作「fork-unsafe函數」。反之,不能引發問題的函數叫作「fork-safe函數」。線程
隨便說一下,malloc 函數就是一個維持自身固有 mutex 的典型例子,一般狀況下它是 fork-unsafe 的。依賴於 malloc 函數的函數有不少,例如 printf 函數等,也是變成 fork-unsafe 的。code
直到目前爲止,已經寫上了thread+fork是危險的,可是有一個特例須要告訴你們「fork 後立刻調用 exec 的場合,是做爲一個特列不會產生問題的」。exec 函數一被調用,進程的「內存數據」就被臨時重置成很是漂亮的狀態。所以,即便在多線程狀態的進程裏,fork 後不立刻調用一切危險的函數,只是調用 exec 的話,子進程將不會產生任何的誤動做。htm
如何規避災難呢?blog
規避方法1:作fork的時候,在它以前讓其餘的線程徹底終止繼承
規避方法2:fork後在子進程中立刻調用exec函數進程
規避方法3:「其餘線程」中,不作fork-unsafe的處理內存
規避方法4: 使用pthread_atfork函數,在即將fork以前調用事先準備的回調函數
規避方法5:在多線程程序裏,不使用 fork