多进程死锁,利用信号将锁释放

如果对本文内容不太熟悉的,可以先查看前两篇博客;
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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值