锁机制

1. 原子操作

  内核提供两组原子操作的接口,一组是对整数进行操作,一组是对单独的位进行操作。

 

现实中,每个临界区不仅仅是对变量的增加、减少,可能临界区域甚至跨越几个函数,而这些都需要保证原子性,因此引入各种锁机制。

2. 自旋锁

  linux内核中最常见的锁是自旋锁。自旋锁最多只能被一个可执行线程持有。

  注意:

  (1) 自旋锁不能递归加锁,否则产生死锁。

  (2) 自旋锁适用于加锁时间短,执行命令少的场合。

  (3) 在单处理器的系统中,编译时并不会加入自旋锁,它仅仅被设置成一个设置内核抢占机制是否被启动的开关。

  (4) 不能再可以引起休眠的地方占用自旋锁,休眠意味着让其CPU,这样将永远没有机会对持有的自旋锁解锁。

 

3. 信号量

  Linux中的信号量是一种可以睡眠的锁。当一个任务试图获取一个不可用(已经被抢占)的信号量时,信号量将它放入等待队列,让其睡眠。等持有的信号量被释放时,处于等待队列的任务将被唤醒,并获得该信号量。

  3.1 计数信号量

    信号量可以同时允许任意数量的锁持有者,而自旋锁只能在一个时刻最多允许有一个任务持有它。

  3.2 二值信号量

    信号量中的计数指定为1,也叫互斥信号量。

 

  P操作:通过对信号量的计数减一来获取信号量,当返回值等于或者大于零时,获取信号量,否则将它放入等待队列。

  V操作:通过对计数加一释放信号量,当信号量的等待队列中的任务不为零,该任务将被唤醒并获得信号量。

 

  注意:

  (1) 因为竞争信号量的进程在等待信号量时会进入睡眠,所有信号量适用于被长时间持有的情况。

  (2) 由于执行线程在锁被争取的时候会引起睡眠,所以只能在进程上下文中获取信号量,因为在中断上下文中是不能被调度的。

  (3) 在占用信号量时不能占用自旋锁,因为在等待信号量时可能引起休眠,而持有自旋锁时不能休眠。

 

4. 互斥体

  "互斥体(mutex)"这个称谓所指是任何可以睡眠的强制互斥锁,比如使用计数是1的信号量。

  

  注意:

  (1) 任何时刻中只有一个任务可以持有mutex,mutex的使用计数永远1。

  (2) 给mutex上锁者必须负责其再解锁----你不能在一个上下文中锁定一个mutex,而在另一个上下文给它解锁。

  (3) 不能递归上锁和解锁。

  (4) mutex不能再中断或者下半部使用。

 

对比:

  信号量和互斥体:

  除非互斥体的某个约束妨碍你的使用,否则优先使用mutex。

 

  自旋锁与互斥体:

  自旋锁:低开销加锁、短时间持有、中断上下文中使用

  互斥体:长时间持有、持有的锁需要睡眠

  

  

posted @ 2014-11-21 12:13  Achille·Swn  阅读(471)  评论(0编辑  收藏  举报