如果对本文内容不太熟悉的,可以先查看前两篇博客;
https://blog.csdn.net/So_Band/article/details/124325694?spm=1001.2014.3001.5502
https://blog.csdn.net/So_Band/article/details/124328512?spm=1001.2014.3001.5502
原因
在多进程的情况下,一个进程获取了锁,但是由于某种原因突然crash了,应该如何解决,本文利用waitpid来解决相应的进程是否死锁,并且释放其相应的锁。
死锁造成
本文中,创建5个子进程,并利用一个数组存储相应的pid,先将每一个子进程循环一遍事务,其中第三个子进程第一次执行的时候是可以正常执行的,没有发生段错误,在第三个子进程第二次执行的时候,会发生段错误,并且其携带了锁,导致其他锁无法释放;以下为代码;
dead_lock.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
char *test_data = "xxx";
pid_t create_fork(int time)
{
int i = 0;
pid_t pid = fork();
if(pid == 0) // 此时为子进程的内容
{
while(1)
{
printf("fork sucess, this is son process, pid=%d, nums = %d\n",getpid(), time);
mylock();
printf("lock pid = %d \n", time);
if(time == 3) // 让其工作一次,然后因为第二次产生段错误
{
if(test_data == NULL)
{
printf("I got lock and break nums = %d",time);
}
char c = *test_data;
test_data = NULL;
}
printf("I am working pid = %d\n", time);
printf("unlock pid = %d \n", time);
myunlock();
sleep(5);
}
}
else if(pid > 0)
{
return pid;
}
else
{
perror("error");
}
}
int main()
{
// 初始化锁
if (mylock_init() < 0) {
exit(1);
}
pid_t p;
pid_t store_[5];
for(int i = 0; i < 5; ++i)
{
store_[i] = create_fork(i); // 创建进程,并存储对应的pid
}
if(waitpid(p,NULL,0))
{
printf("all destroy\n");
}
return 0;
}
mylock.h
#ifndef __MYLOCK_H__
#define __MYLOCK_H__
int mylock_init(void);
void mylock(void);
void myunlock(void);
#endif
mylock.c
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/mman.h>
#include "mylock.h"
static pthread_mutex_t *mptr;
int mylock_init(void)
{
pthread_mutexattr_t mattr;
mptr = mmap(0, sizeof(pthread_mutex_t), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
if (mptr == MAP_FAILED) {
return -1;
}
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mptr, &mattr);
return 1;
}
void mylock(void)
{
pthread_mutex_lock(mptr);
}
void myunlock(void)
{
pthread_mutex_unlock(mptr);
}
运行命令:
gcc -g -o dead_lock ./dead_lock.c ./mylock.c -lpthread
运行结果:从结果可以知道,第一次工作任务都是正常的,第二次工作任务中,第三个工作进程拿走了锁,导致0,1,2,4没有拿到锁,卡死在结果中;
ork sucess, this is son process, pid=8373, nums = 0
lock pid = 0
I am working pid = 0
unlock pid = 0
fork sucess, this is son process, pid=8374, nums = 1
lock pid = 1
I am working pid = 1
unlock pid = 1
fork sucess, this is son process, pid=8375, nums = 2
lock pid = 2
I am working pid = 2
unlock pid = 2
fork sucess, this is son process, pid=8377, nums = 4
fork sucess, this is son process, pid=8376, nums = 3
lock pid = 4
I am working pid = 4
unlock pid = 4
lock pid = 3
I am working pid = 3
unlock pid = 3
fork sucess, this is son process, pid=8373, nums = 0
fork sucess, this is son process, pid=8374, nums = 1
lock pid = 0
I am working pid = 0
unlock pid = 0
lock pid = 1
I am working pid = 1
unlock pid = 1
fork sucess, this is son process, pid=8375, nums = 2
fork sucess, this is son process, pid=8377, nums = 4
lock pid = 2
I am working pid = 2
unlock pid = 2
fork sucess, this is son process, pid=8376, nums = 3
lock pid = 3
fork sucess, this is son process, pid=8374, nums = 1
fork sucess, this is son process, pid=8373, nums = 0
fork sucess, this is son process, pid=8375, nums = 2
解决
利用waitpid来查看当前的进程是否已经挂掉了,如果已经挂掉,再查看当前的锁是否为挂掉的进程所持有,映射一块区域用于存放相应的工作id。
其中waitpid中的WNOHANG,它的作用是防止waitpid把调用者的执行挂起。
具体内容实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "mylock.h"
int *hold_lock_pid = NULL;
char *test_data = "xxx";
pid_t create_fork(int time)
{
int i = 0;
pid_t pid = fork();
if(pid == 0) // 此时为子进程的内容
{
while(1)
{
printf("fork sucess, this is son process, pid=%d, nums = %d\n",getpid(), time);
mylock();
printf("lock pid = %d \n", time);
*hold_lock_pid = time; // 保存当下是由哪个工作任务获取到进程
if(time == 3) // 让其工作一次,然后因为第二次产生段错误
{
if(test_data == NULL)
{
printf("I got lock and break nums = %d",time);
}
char c = *test_data;
test_data = NULL;
}
printf("I am working pid = %d\n", time);
printf("unlock pid = %d \n", time);
myunlock();
sleep(5);
}
}
else if(pid > 0)
{
return pid;
}
else
{
perror("error");
}
return -1;
}
int main()
{
hold_lock_pid = mmap(0, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0); if(hold_lock_pid == MAP_FAILED)
{
exit(1);
}
// 初始化锁
if (mylock_init() < 0) {
exit(1);
}
pid_t p;
pid_t store_[5];
for(int i = 0; i < 5; ++i)
{
store_[i] = create_fork(i); // 创建进程,并存储对应的pid
}
while(1)
{
for(int i = 0; i < 5; ++i)
{
int status;
if(store_[i] == 0) // 当进程为0 ,表明以及死亡
continue;
if(waitpid(store_[i], &status,WNOHANG))
{
if(WIFSIGNALED(status) != 0 && *hold_lock_pid == i)
{// 表明被异常退出, 并且由这个编号拿到相应的锁,则将其释放
store_[i] = 0;
printf("工作任务%d, 释放相应的锁资源\n",i);
myunlock(); // 释放锁
}
}
sleep(1);
}
}
return 0;
}
最后结果输出为:可以看到工作任务3, 释放相应的锁资源,然后只有0,1,2,4任务在执行
fork sucess, this is son process, pid=2729, nums = 0
lock pid = 0
I am working pid = 0
unlock pid = 0
fork sucess, this is son process, pid=2730, nums = 1
lock pid = 1
I am working pid = 1
unlock pid = 1
fork sucess, this is son process, pid=2731, nums = 2
lock pid = 2
I am working pid = 2
unlock pid = 2
fork sucess, this is son process, pid=2732, nums = 3
lock pid = 3
I am working pid = 3
unlock pid = 3
fork sucess, this is son process, pid=2733, nums = 4
lock pid = 4
I am working pid = 4
unlock pid = 4
fork sucess, this is son process, pid=2729, nums = 0
fork sucess, this is son process, pid=2730, nums = 1
lock pid = 0
I am working pid = 0
unlock pid = 0
fork sucess, this is son process, pid=2731, nums = 2
lock pid = 2
I am working pid = 2
unlock pid = 2
fork sucess, this is son process, pid=2733, nums = 4
fork sucess, this is son process, pid=2732, nums = 3
lock pid = 4
I am working pid = 4
unlock pid = 4
lock pid = 1
I am working pid = 1
unlock pid = 1
lock pid = 3
工作任务3, 释放相应的锁资源
fork sucess, this is son process, pid=2729, nums = 0
lock pid = 0
I am working pid = 0
fork sucess, this is son process, pid=2731, nums = 2
unlock pid = 0
fork sucess, this is son process, pid=2733, nums = 4
fork sucess, this is son process, pid=2730, nums = 1
lock pid = 2
I am working pid = 2
unlock pid = 2
lock pid = 4
I am working pid = 4
unlock pid = 4
lock pid = 1
I am working pid = 1
unlock pid = 1
fork sucess, this is son process, pid=2731, nums = 2
fork sucess, this is son process, pid=2729, nums = 0
lock pid = 2
I am working pid = 2
fork sucess, this is son process, pid=2733, nums = 4
unlock pid = 2
fork sucess, this is son process, pid=2730, nums = 1
lock pid = 0
I am working pid = 0
unlock pid = 0
lock pid = 4
I am working pid = 4
unlock pid = 4
lock pid = 1
I am working pid = 1
unlock pid = 1