并发访问共享数据是造成系统不稳定的一类隐患,而且这种错误难以跟踪和调试,所以处理内核并发问题非常重要。
产生并发的原因---->解决办法----->解决办法存在的问题
产生并发的原因
- 中断----中断几乎可以在任何时刻异步发生,可能随时打断当前正在执行的代码。
- 软中断和tasklet----内核能在任何时刻唤醒或调度软中断和tasklet,打断当前正在执行的代码。
- 睡眠及与用户空间的同步----在内核执行的进程可能会睡眠,这会唤醒调度程序,从而导致调度一个新的用户进程执行。
- 内核抢占----内核具有抢占性,所以内核中的任务可能会被另一任务抢占。 对称多处理----两个或多个处理器可以同时执行代码。
锁(解决办法)
找出哪些数据需要保护是关键所在。大多数内核数据结构都需要加锁,要给数据加锁而不是给代码加锁。
解决办法存在的问题
1、死锁
锁在解决并发和同步问题时,可能出现死锁。死锁产生需要一定条件:有一个或多个执行线程和一个或多个资源,每个线程都在等待其中的一个资源,但所有的资源都已经被占用了。所有线程都在相互等待,但永远不会释放已经占有的资源。于是任何线程都无法继续,这意味着死锁的发生。
避免死锁的方法
- 按顺序加锁。使用嵌套锁时必须保证以相同的顺序获取锁,这样可以阻止致命拥抱类型的死锁。最好记录下锁的顺序,以便其他人也能照此顺序使用。
- 防止发生饥饿。试问这个代码的执行是否一定会结束?如果“张”不发生,“王”要一直等待下去吗?
- 不要重复请求同一个锁。
- 设计力求简单----越复杂的加锁方案越有可能造成死锁。
2、争用和扩展性
(1)锁的争用
由于锁的作用是使程序以串行方式对资源进行访问,所以锁会降低系统性能。
被高度争用的锁会成为系统的瓶颈,严重降低系统性能。
(2)扩展性
任何可被计量的计算机组件如大量进程、大量处理器或是大量内存都可以涉及可扩展性。理想情况下,处理器的数量加倍会使系统处理性能翻倍,而实际这是不可能达到的。
许多锁的设计开始阶段都很粗,当锁的争用变得严重时,设计就向更加精细的加锁方向进化,进而增加了可扩展性。但一味提高可扩展性,会导致linux在小型SMP机器上的性能降低,锁得过细增加复杂度,加大开销。
对重要资源锁得太粗,容易产生争用,降低可扩展性,造成系统性能瓶颈。当锁争用不明显时,锁得过细会加大系统开销,带来浪费。