Linux内核设计与实现 总结笔记(第九章)内核同步介绍

在使用共享内存的应用程序中,程序员必须特别留意保护共享资源,防止共享资源并发访问。程序员

1、临界区和竞争条件

1.1 临界区和竞争条件

所谓临界区就是访问和操做共享数据代码段。多个执行线程并发访问同一个资源一般是不安全的,为了不在临界区中并发访问,编程者必须保证这些代码原子地执行。编程

若是两个执行线程有可能处于同一个临界区中同时执行,若是这种状况确实发生了,咱们就称它是竞争条件。由于竞争引发的错误很是不易重现,因此调试这种错误才会很是困难。安全

避免并发和防止竞争条件称为同步。数据结构

 

为何要保护?并发

有些事务必须完整地发生,要么干脆不发生,可是毫不能打断。异步

2、加锁

锁提供的就是这种机制:它就如同一把门锁,门后的房间可想象成要给临界区。在一个指定的时间内,房间里只能有一个执行线程存在,当一个线程进入房间后,它会锁住身后的房门。当它结束对共享数据的操做后,就会走出房间,打开门锁。若是另外一个线程在房门上锁时来了,那么它就必须等待房间内的线程出来并发开门锁后,才能进入房间。函数

线程持有锁,锁保护了数据。工具

2.1 形成并发执行的缘由

内核中有相似可能形成并发执行的缘由,它们是:线程

  • 中断----中断几乎能够在任什么时候刻异步发生,也就可能随时打断当前正在执行的代码
  • 软中断和tasklet----内核能在任什么时候刻唤醒或调度软中断和tasklet,打断当前正在执行的代码
  • 内核抢占----由于内核具备抢占型,因此内核中的任务可能会被另外一任务抢占
  • 睡眠及与用户空间的同步----在内核执行的进程可能会睡眠,这就会唤醒调度程序,从而致使调度一个新的用户进程执行
  • 对称多处理----两个或多个处理器能够同时执行代码

真正困难的就是发现上述的潜在并发执行的可能,并有意识地采起某些措施来防止并发执行。设计

其实,真正用锁来保护共享资源并不困难,尤为是在设计代码的早期就这么作了,事情就更简单了。

辨认出真正须要共享的数据和响应的临界区,才是真正有挑战性的地方。

 

2.2 了解要保护些什么

找出哪些数据须要保护是关键所在。

线程中的局部数据仅仅被它访问,显然不须要保护。还有动态分配的数据结构,其地址在堆栈中。也不须要

大多数内核数据结构都须要加锁,

若是有其余执行线程能够访问这些数据,那么就给这些数据加上某种形式的锁。

若是任何其余什么东西都能看到它,那么就要锁住它。记住:要给数据而不是给代码加锁。

在编写代码时,你要问本身下面这些问题:

  • 这个数据是否是全局的?除了当前线程外,其余线程能不能访问它。
  • 这个数据会不会在进程上下文和中断上下文中共享?它是否是要在两个不一样的中断处理程序中共享?
  • 进程在访问数据时可不可能被抢占?被调度的新程序会不会访问同一数据?
  • 当前进程是否是会睡眠(阻塞)在某些资源上,若是是,它会让共享数据处于核中状态?
  • 怎样防止数据失控?
  • 若是这个函数又在另外一个处理器上被调度将会发生什么呢?
  • 如何确保代码原理并发威胁呢?

简而言之,全部内核全局变量和共享数据都须要某种形式的同步方法。

 

3、死锁

死锁产生须要必定条件:要有一个或多个执行线程和一个或多个资源,每一个线程都在等待其中的一个资源,但全部资源都已经被占用了。

自死锁:一个执行线程试图去得到要给本身已经持有的锁,它将不得不等待锁被释放。最后智能死锁

一些简单的规则对避免死锁大有帮助:

  • 按顺序加锁,使用嵌套的锁时必须保证以相同的顺序获取锁,这样能够阻止致命拥抱类型的死锁。最好能记录下锁的顺序,以便其余人也能照此顺序使用
  • 防止发饥饿,这个代码是否是必定能执行结束?若是张不发生,王是否要一直等待下去
  • 不要重复请求同一个锁
  • 设计应力求简单----越复杂的加锁方案越有可能死锁

防止死锁很重要,内核提供了一些简单易用的调试工具,能够在运行时检测死锁。

4、争用和扩展性

锁的争用,简称争用,指当锁正在被占用时,有其余线程试图得到该锁。

当一个锁处于高度争用状态,会成为系统的瓶颈。尽管如此,也比多个进程抢夺资源要好。

锁的粒度,须要合理的安排。不少锁的设计在开始阶段都很粗,可是当锁争用问题严重时,设计就向更加精细的加锁方向进化。

相关文章
相关标签/搜索