问题: 父进程先开启一个子线程,子线程中调用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, 即才进入子进程)。