當線程調用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