atfork() --同步父子程序 pthread_mutex_lock加解鎖問題
問題: 父程序先開啟一個子執行緒,子執行緒中呼叫pthread_mutex_lock。再fork子程序,子程序同樣呼叫pthread_mutex_lock ,導致的死鎖問題。
sleep() 替換 nanosleep() 納秒精度
//sleep(1);
struct timespec ts = {1, 0};
nanosleep(&ts, NULL);
一、未呼叫atfork(),導致子程序呼叫pthread_mutex_lock 時發生死鎖
#include <pthread.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <wait.h> pthread_mutex_t mutex; void *another(void *arg) { printf("in child thread, lock the mutex\n"); pthread_mutex_lock(&mutex); printf("parent lock..\n"); sleep(5); pthread_mutex_unlock(&mutex); printf("parent unlock..\n"); // 解鎖兩次,結果未定義 //pthread_mutex_unlock(&mutex); //printf("mutex too.\n"); } void prepare(void) { pthread_mutex_unlock(&mutex); } void infork(void) { pthread_mutex_lock(&mutex); } int main() { pthread_mutex_init(&mutex, NULL); pthread_t id; pthread_create(&id, NULL, another, NULL); //pthread_atfork(prepare, NULL, NULL); #if 1 sleep(1); int pid = fork(); if (pid < 0) { pthread_join(id, NULL); pthread_mutex_destroy(&mutex); return 1; } else if (pid == 0) { printf("I am in the child, want to get the lock\n"); pthread_mutex_lock(&mutex); //printf("I can not run to here, oop...\n"); printf("child lock..\n"); sleep(5); pthread_mutex_unlock(&mutex); printf("child unlock..\n"); exit(0); } else { wait(NULL); } printf("join...\n"); #endif pthread_join(id, NULL); pthread_mutex_destroy(&mutex); return 0; }
二、子程序在子執行緒釋放鎖前便可執行。
呼叫atfork(), 但prepare函式為解鎖,parent 和child呼叫的函式為加鎖 (infork)
prepare: 在呼叫fork 生成子程序之前,先呼叫prepare,解鎖,因此子程序便可直接呼叫pthread_mutex_lock(),
parent: fork呼叫創建出子程序之後,而fork返回之前,在父程序中被執行。
child: 在fork返回之前,在子程序中被執行
以上均成功時返回 0,失敗是返回錯誤碼。
void prepare(void) { pthread_mutex_unlock(&mutex); } void infork(void) { pthread_mutex_lock(&mutex); }
在呼叫 fork之前,先呼叫atfork();
pthread_atfork(prepare, infork, NULL);
程式執行結果:
解析: 1、在子程序呼叫lock之前(即fork之前,先執行prepare),先執行prepare 解鎖, 於是子程序中便可呼叫lock,進入sleep(5),
2、父程序執行parent 函式 (fork呼叫創建出子程序之後,而fork返回之前,在父程序中被執行。)執行加鎖, 父程序中的子執行緒睡眠之後,便可解鎖
3、pthread_atfork(), 第三個引數為NULL , 本應該為child() 即 infork()執行加鎖操作,若新增,在fork返回之前,先進行lock操作(child執行),然後子程序又一次執行 lock(子程序中的lock), 便發生死鎖。
三、子程序在子執行緒釋放鎖後才可執行。
呼叫atfork(), 但prepare函式為加鎖,parent 和child呼叫的函式為解鎖 (infork)
void prepare(void)
{
pthread_mutex_lock(&mutex);
}
void infork(void)
{
pthread_mutex_unlock(&mutex);
}
pthread_atfork(prepare, infork, infork);
空格為執行順序,表示睡眠sleep(手動輸入表示)
分析:同上, prepare先執行加鎖,因此在子程序呼叫lock之前 就已被鎖住, 待父程序中的子執行緒執行完畢,解鎖之後,fork才返回(才開始執行 I am in the child, want to get the lock, 即才進入子程序)。