當線程調用fork時,就為子進程創建了整個進程地址空間的副本。子進程通過繼承整個地址空間的副本,也從父進程那裡繼承了所有互斥量、讀寫鎖和條件變量的狀態。如果父進程包含多個線程,子進程在fork返回以後,如果緊接著不是馬上調用exec的話,就需要清理鎖的狀態。
在子進程內部只存在一個線程,它是由父進程中調用fork的線程的副本構成的。如果父進程中的線程占有鎖,子進程同樣占有這些鎖。問題是子進程並不包含占有鎖的線程的副本,所以子進程沒有辦法知道它占有了哪些鎖,並且需要釋放哪些鎖。
當多線程進程調用fork創建子進程時,Pthreads指定只有那個調用fork的線程在子進程內存在(表示子進程中只有調用線程這個線程)。盡管當從fork調用返回時,只有調用線程在子進程中存在,所有其他的Pthreads線程狀態(互斥量、讀寫鎖和條件變量的狀態以及線程私有數據鍵)仍保留為與調用fork時相同的狀態。在子進程中,線程擁有與在父進程內相同的狀態。
注:fork調用不會影響互斥量的狀態。如果它在父進程中被鎖住,則它在子進程中被鎖!
因為沒有調用線程私有數據銷毀和清除處理函數,你可能需要擔心存儲洩漏問題。
1.fork處理器
int pthread_atfork(void (*prepare)(void),void (*parent)(void),void(*child)(void));//返回值:若成功則返回0,否則返回錯誤編號
Pthreads增加了pthread_atfork ”fork處理器”機制以允許你的代碼越過fork調用保護數據和不變量。這與atexit有點類似,後者在一個進程終止時允許程序執行清除操作。
查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/
使用pthread_atfork,你需要提供三個獨立的處理函數地址。prepare fork處理程序由父進程調用fork創建子進程之前調用,這個fork處理程序的任務是獲得父進程定義所有的鎖。parent fork處理程序在fork創建了子進程以後,但在fork返回之前在父進程環境中調用,這個fork處理程序的任務是對prepare fork處理程序獲得的所有鎖進行解鎖。child fork處理程序在fork返回之前在子進程環境中調用,與parent fork處理程序一樣,child fork處理程序也必須釋放parent fork處理程序獲得的所有鎖。
可以調用pthread_atfork多次注冊多組回調函數,這時,回調函數調用的順序規定如下:
prepare函數調用順序與它們的注冊順序相反;
parent和child函數的調用順序與注冊順序相同。
示例代碼:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <pthread.h> pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER; void prepare(void) { printf("preparing locks...\n"); pthread_mutex_lock(&lock1); pthread_mutex_lock(&lock2); } void parent(void) { printf("parent unlocking locks...\n"); pthread_mutex_unlock(&lock1); pthread_mutex_unlock(&lock2); } void child(void) { printf("child unlocking locks...\n"); pthread_mutex_unlock(&lock1); pthread_mutex_unlock(&lock2); } void *thr_fn(void *fn) { printf("thread started...\n"); pause(); return 0; } int main(void) { pid_t pid; pthread_t tid; //BSD系統和MAC OS系統不支持pthread_atfork #if defined(BSD) || defined(MACOS) printf("pthread_atfork is unsupported\n"); #else pthread_atfork(prepare, parent, child); pthread_create(&tid, NULL, thr_fn, NULL); sleep(2); printf("parent about to fork...\n"); pid = fork(); if( 0 == pid ) printf("child returned from fork\n"); else { printf("parent returned from fork\n"); wait(NULL); } #endif return 0; }
運行結果:
huangcheng@ubuntu:~$ ./a.out thread started... parent about to fork... preparing locks... parent unlocking locks... parent returned from fork child unlocking locks... child returned from fork
作者:csdn博客 ctthuangcheng