Linux之死锁产生条件&解决方法

本文详细介绍了Linux系统中死锁的概念、产生的四个必要条件,包括互斥条件、持有并请求、不可剥夺和环路等待。接着讨论了预防死锁的策略,如一次性分配资源和资源有序分配法。同时,提到了解决死锁的银行家算法以及死锁的预防和恢复方法,如资源掠夺和进程撤销。最后通过线程锁的例子展示了死锁的避免对于确保程序正确性的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

何为死锁:

  现有A、B两个进程和a,b两把锁,a锁保护a资源,b锁保护b资源,现在A申请a资源,则a锁上锁,B申请b资源,则b锁上锁。此时,A又需要b资源,对b资源进行申请,但B正在使用,即b锁已经上锁,没有申请到,即挂起等待,同时,B需要a资源,对a资源进行申请,但A已经挂起,并且A持有a资源,所以B不可能申请到,所以B也 挂起。此时、B都挂起——死锁。

死锁产生的四个必要条件

  1、互斥条件——一个资源一次只能被一个进程(或线程)使用;
  2、持有——当请求的资源被占用导致执行阻塞时,持有者不仅 可以 不释放资源,还可以申请更多资源(这句话理解为:当前系统共有多个进程或线程申请1这份资源,但1这份资源被A进程拿着,此时等待1资源的进程或线程均被挂起等待,且此时A还可以申请2,3,4,5,6等等资源,这对操作系统来说,完全符合规定);
  3、不可剥夺——只有资源持有者才有权释放该资源;
  4、环路等待——若干进程或线程以不同的次序获得互斥资源,而形成的环路等待,即当前进程总在等待上一个进程释放的资源。
  这里写图片描述

预防死锁的方法:
  
  产生死锁共有4中方法,根据这四种方法,就可以针对性的进行解决。
  1、互斥条件,这是防止多进程、多线程间不产生冲突的根本所在,所以不能进行改变。
  2、持有,在创建进程或线程时就一次性分配这么多的资源,如果当前存在这么多资源,则允许其运行,如果没有,则等待;每次在申请新资源时,必须释放他之前所占有的资源。
  3、不可剥夺,如果某个进程或线程在持有某个资源的同时,申请另外的资源,则必须释放最初占有的资源,若后续还会用到,则再次申请;或者根据抢占的原理,即再优先级高于对方的情况下,如果对方只是申请资源,并没有使用资源,则可以对这份资源进行抢占。
  4、对所有资源进行统一编号。进程读资源的申请必须以资源编号的升序方式提出。
 

解决死锁的几种算法

  1、有序资源分配法
    所有资源的使用都要有序的使用,要保证:
    1>对它所必须使用的而且属于同一类的所有资源,必须一次申请完;
    2>在申请不同类资源时,必须按各类设备的编号依次申请。例如:进程PA,使用资源的顺序是R1,R2; 进程PB,使用资源的顺序   是R2,R1;若采用动态分配有可能形成环路条件,造成死锁。这就可以完美解决开篇的第一个问题。
  2、银行家算法
    避免死锁算法中最有代表性的算法是Dijkstra E.W 于1968年提出的银行家算法。
    所谓的银行家算法就是:一个银行家的借贷业务,必须保证其借贷人在一定时间内归还本金,以至于资金可以顺利周转,不至于倒闭。
  在操作系统中,操作系统就相当于银行家,所有的资源就是他的本金。系统中有限的资源要供多个进程使用,必须保证得到资源的进程能在有限的时间内归还资源,以供其他进程使用资源。如果资源分配不得到就会发生进程循环等待资源,则进程都无法继续执行下去的死锁现象。
  
  

死锁的预防和恢复

  1、资源掠夺法:当前系统中存在多个死锁程序时,选择葫芦一部分死锁程序,并抢占他的资源,将这些稀有分配给其他进程使用,直到死锁不存在为止;
  2、撤销进程法:放弃处于死锁进程中的进程,直接撤销该进程,直到死锁不存在为止;
  3、进程回退法:进程自按获取顺序自愿放弃获取的资源,系统保存进程的历史信息,设置还原点。

简单的线程锁的实现:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_t tid1,tid2;
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
unsigned int count=0; 

void* add1(void *val)
{
    int i=5000;
    int value=0;
    while(i--)
    {   
        pthread_mutex_trylock(&lock);

        value=count;
        printf("add1:%u count:%u\n",pthread_self(),count);
        count=value+1;

        pthread_mutex_unlock(&lock);
    }
}
void* add2(void *val)
{

    int i=5000;
    int value=0;
    while(i--)
    {   
        pthread_mutex_lock(&lock);

        value=count;
        printf("add1:%u count:%u\n",pthread_self(),count);
        count=value+1;

        pthread_mutex_unlock(&lock);
    }
}

int main()
{

    pthread_create(&tid1,NULL,add1,NULL);
    pthread_create(&tid2,NULL,add2,NULL);

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    printf("final count is %u\n",count);
}

不加锁的输出:
每次运行过后的结果都有差异,且不会达到10000,因为加一的操作不是原子操作,这个在我之前的博客里面有提过。
这里写图片描述

这里写图片描述

这里写图片描述
加锁后的输出:
多次运算过后始终未10000。

这里写图片描述

因为此时这里执行的操作为:
这里写图片描述
而xchgc一步达成!

如有错误,请指出!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值