多線程程序裏不許使用fork

多線程程序裏不許使用fork

通常的,fork作以下事情html

  1. 父進程的內存數據會原封不動的拷貝到子進程中
  2. 子進程在單線程狀態下被生成

在內存區域裏,靜態變量 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;
}

死鎖緣由的詳細解釋 ---函數

  1. 線程裏的 doit() 先執行
  2. doit() 執行的時候會給互斥體變量 mutex 加鎖
  3. mutex 變量的內容會原樣拷貝到 fork 出來的子進程中(在此以前,mutex 變量的內容已經被線程改寫成鎖定狀態)
  4. 子進程再次調用 doit() 的時候,在鎖定互斥體 mutex 的時候會發現它已經被加鎖,因此就一直等待,直到擁有該互斥體的進程釋放它(實際上沒有人擁有這個 mutex 鎖)
  5. 線程的 doit() 執行完成以前會把本身的 mutex 釋放,但這是的 mutex 和子進程裏的 mutex 已是兩分內存。因此即便釋放了 mutex 鎖也不會對子進程裏的 mutex 形成什麼影響

像這裏的 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

參考

http://www.cppblog.com/lymons/archive/2008/06/01/51836.html

相關文章
相關標籤/搜索