死锁的条件、原因以及场景分析

死锁的条件、原因以及场景分析

死锁可以称为进程死锁。那么是在多进程(并发)情况下可能会出现的。

指的是多个进程因为竞争资源而造成的僵局(互相等待),没有外力,那么所有进程都会无法向前推进。

所以是在操作系统和并发程序设计中需要特别考虑的问题。

因此,可以可以得出如下的场景和必备条件。

场景:

  • 系统资源的竞争。只有资源不足时才会出现死锁可能,另外,可剥夺资源的竞争是不会引发死锁的;
  • 进程推进顺序不对。多进程在运行时,请求和释放资源的顺序不当。
  • 系统资源分配不当。

四大必要条件:

  • 互斥:进程对分配到的资源排它性使用。独占资源,是由资源本身的属性决定的。
  • 请求和保持:保持已有资源,同时请求新的资源,在请求过程中以及因为没有得到新资源而阻塞,已有资源仍然保持;
  • 不可剥夺:进程已有的资源在使用完之前不能被剥夺,只能自己释放;
  • 环路等待:必然存在一个进程资源环形请求链。

死锁预防:打破之前四个条件

  • 打破互斥在实际中应用不大;
  • 打破请求与保持,可以实行资源预先分配策略,即进程在运行前一次性申请所需要的全部资源,如果不能满足,则暂不运行。实际应用中,进程在执行时是动态的,不可预测的,并且资源利用率低,降低了进程并发性。
  • 打破不可剥夺,当请求新资源不能满足,需要释放已有资源,系统性能受到很大降低
  • 打破循环等待:实行资源有序分配策略。可以将资源事先分类编号,按号分配,使进程在申请、占用资源是不会形成环路。所有进程对资源的请求必须严格按资源序号递增的顺序提出。但是也有问题,合理编号困难,增大系统开销,另外也增加了进程对资源的占有时间。

死锁避免:

  不限制进程有关申请资源的命令,而是对进程所发出的每一个申请资源命令加以动态地检查,并根据检查结果决定是否进行资源分配。就是说,在资源分配过程中若预测有发生死锁的可能性,则加以避免。这种方法的关键是确定资源分配的安全性。

  银行家算法(1968年):允许进程动态地申请资源,系统在每次实施资源分配之前,先计算资源分配的安全性,若此次资源分配安全(即资源分配后,系统能按某种顺序来为每个进程分配其所需的资源,直至最大需求,使每个进程都可以顺利地完成),便将资源分配给进程,否则不分配资源,让进程等待。

死锁检测与修复:

  预防和避免的手段达到排除死锁的目的是很困难的。一种简便的方法是系统为进程分配资源时,不采取任何限制性措施,但是提供了检测和解脱死锁的手段:能发现死锁并从死锁状态中恢复出来。因此,在实际的操作系统中往往采用死锁的检测与恢复方法来排除死锁。

----------------------------------------------------------------------------------------------------

死锁常见的场景如下:

忘记释放锁:

1

2

3

4

5

6

7

8

9

void data_process()

{

    EnterCriticalSection();

    if(/* error happens, forget LeaveCriticalSection */)

        return;

    LeaveCriticalSection();

}

  

单线程重复申请锁(所以单线程的时候也有可能进入死锁)

1

2

3

4

5

6

7

8

9

10

11

12

13

void sub_func()

{

    EnterCriticalSection();

    do_something();

    LeaveCriticalSection();

}

void data_process()

{

    EnterCriticalSection();

    sub_func();

    LeaveCriticalSection();

}

  

多线程多锁申请

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

void data_process1()

{

    EnterCriticalSection(&cs1);  // 申请锁的顺序有依赖

    EnterCriticalSection(&cs2);

    do_something1();

    LeaveCriticalSection(&cs2);

    LeaveCriticalSection(&cs1);

}

void data_process2()

{

    EnterCriticalSection(&cs2);  // 申请锁的顺序有依赖

    EnterCriticalSection(&cs1);

    do_something2();

    LeaveCriticalSection(&cs1);

    LeaveCriticalSection(&cs2);

}

  

多线程环形锁

编程中如何来避免:

  • 检查有没有忘记释放锁
  • 如果自己模块可能重复使用一个锁,建议使用嵌套锁
  • 建议使用库里面的锁
  • 如果某项业务需要获取多个锁,保证锁按某种顺序来获取
  • 编写简单测试用例验证是否存在死锁
发布了1 篇原创文章 · 获赞 0 · 访问量 4180

猜你喜欢

转载自blog.csdn.net/b_just/article/details/102659098