https://cloud.tencent.com/developer/article/1561229
问:介绍一下操作系统的死锁?
答:死锁是指两个或多个进程之间相互阻塞,以至于任何一个都不能继续运行,因此也不能解锁其他线程或进程。
例如,线程A占有lock A,并且产生获取lock B;而线程B占有lock B,尝试获取lock A。此时,两者相互阻塞,都无法继续运行。

问:产生死锁的原因
答:

  • 竞争资源;
  • 进程间推进顺序非法或者不合理。进程运行过程中,请求和释放资源的顺序不当。

问:死锁发生的必要条件是什么
答:死锁发生必须具备四个必要条件。

  1. 互斥条件。某资源在一段时间内只能由一个进程占用,如果其他进程需要请求该资源,只能进行等待,直至占有该资源的进程用完释放。
  2. 请求和保持条件:进程已经保持了至少一个资源,又提出了新的资源请求,而该资源又被其他进程占有。
  3. 不剥夺条件:进程获取的资源,在未使用完之前,不能被剥夺,只能自己释放
  4. 环路等待:资源环形链。若干进程之间形成一种头尾相接的循环等待资源关系。

问:如何预防死锁
答:在程序设计时,摒弃上述的2,3,4情况。

问:知道银行家算法吗?
答:银行家算法是用于避免死锁的算法,由Dijkstra提出。(继续细化)


<1>.“死锁”产生有几个必要条件?
“死锁”中“死”音通“四”是四个条件。(虽然很牵强,但对于识记,你会发现很有用!)
<2>.“四”锁的必要条件都是指哪些?
1.资源互斥性(资源的属性,不会同时属于/分配给多个进程)->2.持有和等待(进程被分配(持有)资源后而且在等待(申请)其他资源)->3不可剥夺(对于已分配的资源,不可再强制收回来)->4.环形等待(进程之间资源等待形成相互依赖,互不礼让)
用再通俗的话,进一步解释这个逐渐升级的状况。1->2->3->4是资源分配逐渐条件递进、加强、升级的过程:资源是互斥的每次属于一个进程(1.互斥);
资源分配了我占着而且我还要申请其他资源(2.持有和等待);
我占着的资源你没法拿,你占着的资源我没法拿(3.不可剥夺);
我申请的资源在你那,你申请的资源在我这(4.循环等待),最终形成了一个相互占有、相互等待的僵持局面~

“死锁”的预防和解除

理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁,消除产生死锁的四个必要条件中的任何一个都可以预防和解除死锁。
不难看出,在死锁的四个必要条件中,第二、三和四项条件比较容易消除。
1、静态分配:采用资源静态分配策略(进程资源静态分配方式是指一个进程在建立时就分配了它需要的全部资源),破坏”部分分配”条件;
2、可剥夺:允许进程剥夺使用其他进程占有的资源,从而破坏”不可剥夺”条件;
3、有序分配:采用资源有序分配法,破坏”环路”条件。

背景知识补充

线程类似于独立的进程,有一点区别是:线程共享地址空间,从而能够访问相同的数据。

进程之间上下文切换,将状态保持到进程控制块(ProcessControl Block,PCB)
线程之间上下文切换,将状态保存到线程控制块(Thread Control Block,TCB)
但是,与进程相比,线程之间的上下文切换有一点主要区别:地址空间保持不变(即不需要切换当前使用的页表)。

临界区:访问共享资源的一段代码,资源通常是一个变量或数据结构
竞态条件:出现多个执行线程同时进入临界区,它们都试图更新共享的数据结构,导致了不希望的结果
不确定性:有一个或多个竞态条件组成,程序输出取决于这些线程在何时运行,导致了结果的不确定性

因此,线程应该用互斥原语(mutual exclusion),保证一个线程进入临界区,避免出现竞态,并产生确定的程序输出。

为什么需要锁?
答:我们希望原子式地执行一系列指令,但由于单处理器上的中断(或者多个线程在多处理器上并发执行),做不到原子式执行。为了解决这一问题,在临界区周围加锁,保证临界区能够像单挑原子指令一样执行,即保证临界区只有一个线程活跃。

锁其实就是一个变量。

自旋锁