在应用层可以使用信号量PV操作和互斥锁来实现线程间对临界资源访问的同步与互斥。同样在内核态的驱动程序中,多个执行单元并发执行时也会造成对共享资源的竞争,形成竞态。
linux内核发生竞态的三种情况:
(1)对称多处理器(SMP)的多个CPU,多个CPU共用总线、外设和存储器,必然会造成资源的竞争。
(2)进程与进程之间也有可能对共享资源造成竞争,linux2.6内核以后是支持抢占调度的。
(3)中断和进程之间,中断程序有可能访问当前进程正在访问的资源。
解决办法:
屏蔽中断、原子操作、自旋锁、信号量
1、屏蔽中断
使用方法:
local_irq_disable() //屏蔽中断
....................
临界区
....................
local_irq_enable()//开中断
这种方法使得进程和中断之间竞态消失,同时进程和进程之间的竞态也消失,因为进程调度是依靠中断来实现的。但是,此方法对SMP多CPU引发的竞态则无能为力了。
要求:要尽可能快的执行完临界区代码,中断对CPU至关重要,不能一直关闭。
此方法不常用。
2、原子操作
原子操作指的是在执行过程中不会被别的进程所中断的操作,linux内核提供了两类函数实现原子操作,实现方法参见include/asm/atomic.h 也可参考linux设备驱动开发详解(宋宝华著)P142页。这里不再赘述。
3、自旋锁
所谓自旋,就是当进程不能获得资源时处于忙等待状态,注意不是阻塞态,通俗的说是原地打转。注意:所谓的自旋是对多处理器而言的,单处理器不可能出现自旋状况。
特点:
(1)无法获得锁时处于忙等待(运行态)。
(2)任务获得自旋锁后,内核关抢占。这一点对于单处理器很重要,这就是单处理器使用自旋锁的原因,防止了进程抢断。
(3)一个任务持有锁的时间要尽可能的短。
衍生锁:
读写自旋锁:读与读之间可以同时进行,读与写,写与写之间是互斥的。
顺序锁:读与读,读与写之间是可以同时的,但写与写之间是互斥的。
4、信号量
与自旋锁原理差不多,只是当获取不到信号量时进程不会原地打转,而进入阻塞态。
一般用法:
struct semaphore sem;
sema_init(&sem, 1);
down(&sem);
..................
临界区
..................
up(&sem);
自旋锁 VS 信号量
忙等待(运行态) 等待态
禁止睡眠、调度 允许睡眠
时间尽可能短 时间不限
可用于中断 一般不用于中断
(执行中断时不能睡眠)
解释:自旋锁当无法获取锁时或出于忙等待状态(运行态),不会阻塞,而且获得锁以后会禁止调度,这也就实现了进程间的互斥,由于CPU禁止调度也就导致了要求占用锁的时间要尽可能的短,进程禁止调度是件非常可怕的事情。由于自旋锁不会睡眠,因此可以用于中断。
而信号量,无法获取时会阻塞、睡眠,由于信号量对进程调度没有什么影响,所以他的时间要求也没有自旋锁那么严格。由于执行中断时不允许进程阻塞,中断后会禁止调度,进程再一阻塞,会导致系统死掉,所以信号量不能用于中断。
阅读(3200) | 评论(0) | 转发(0) |