我在马路上遇到一个死锁问题
扫描二维码
随时随地手机看文章
▍马路上的死锁问题
我在市里的老区马路上遇到堵车了,动弹不得,后来发现是个“死锁”问题。
当时我坐在③号车里,等了好久,忽然职业病犯了,好想下车跟他们讲解下发生了什么,“死锁”是怎么造成的,如何避免……
简要情况,见下图,原因是右上方的小路一次只能通过一个方向的车,大路上红色的车可能因为没找到车位,随便停路边了,导致图中①、②、③、④、⑤号车动弹不得。
实际上,在拥堵的马路,如果没有交通指示的话,很容易造成类似这种“死锁”。如下图这种情形。
除了交通上这个案例,我们再来看看网络上的一张趣图(姑且叫人质问题,图来源于https://stackoverflow.com/questions/34512/what-is-a-deadlock叫Criminal & Cop Scene):
扯远了,我们还是认真地聊聊这个“死锁”问题吧。
▍锁的概念
锁是什么?其实概念很简单,就是平时我们见到的锁。只是操作系统仿照现实上的这个概念做了一套逻辑而已。原理,我还是可以从现实生活中去理解的。
例如,你去买衣服,店里有试衣间。当你想试一下衣服的时候,就可能要用到试衣间,你进了试衣间,就要将门锁上,表示你当时占用试衣间这个资源,用完就释放,然后别人就可以用了。试想下,如果你不锁门,代价可能就是春光乍泄了……
如果你买衣服从没进过试衣间,那就自己脑补下上公共厕所的例子吧……
回到软件上的锁概念,一般OS会提供两种接口来访问这个锁,一个是Acquire或者叫Require/Get/Wait,一个Release,即表示占有并锁上资源和释放这个资源。
一般RTOS会用Semaphore或者Mutex来实现这个锁的功能,当然,复杂的系统如Linux会有更多种类的锁,如自旋锁,互斥锁,读写锁等等。
▍锁的作用
这还用说吗,目的只有一个,你占用的时候,不想被被人占有。防止各种各样的问题。例如,上厕所记得把门锁上,不然别人推开了,你会骂人家流氓,或者,人家也骂你流氓,上厕所不锁门……
还有哦,算了,程序员有女朋友吗?有的话,差不多就要去扯证,长期官方锁上……哈哈哈
▍死锁的原因
情景1:嵌套锁/递归锁的使用
上段代码来看看:
task1在第一个Doing someting中被task2抢占,死锁就开始了。这是一种嵌套锁使用遇到的死锁情形。递归锁(如果你真用过,那真有装逼的嫌疑了),也会有这样的问题。
现实项目中,可能会更隐秘,找到它需要花点心思。
情景2:锁还没被释放,Task却被kill了
RTOS设计的没那么智能,task被kill,其所占有的资源不一定会被自动释放。这种情形,很容易发生在关机的流程。设计上一定要非常谨慎。
情景3:Task给自己发mailbox
Tasks之间的通信,我们一般都是用mailbox,同一个task的mailbox接口可以被多个其他task调用,也不会发生资源冲突问题。实际上,mailbox内也隐藏着一个锁的功能,一个task调用了mailbox接口,其他task只能等它调用完了,才能调用。
一般定义的mailbox是一个空间有限的队列,在频繁调用的时候,资源不够,不让正在调用的task在等。这实际上很正常,不正常的时候,如果task自己调用了自己的mailbox接口,那就容易见鬼了。
假如上图的task1优先级很低,定义的mailbox大小为4,都被task2、task3、task4、task5被调用了,而task1还没来得及处理释放mailbox资源。正在此时task1的某些应用代码也需要发一个消息,调用了自己的mailbox接口,然后就死了……
▍死锁的避免
使用锁的注意事项,不详细解释了,总结以下几条:
不允许使用嵌套锁,即不要用锁中锁;
不允许使用递归锁,这个跟嵌套锁类似;
不要在中断服务中使用锁;
Task或者Thread被kill之前,确保其占有的锁已经释放了;
Require资源的时候,尽量不用死等(即超时为Forever)。
-END-
本文授权转载自公众号“嵌入式软件实战派”,作者:实战派大师兄
推荐阅读
免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!