Linux设备驱动中的并发控制

产生竞态的情况:

(1)对称多处理器(SMP)的多个CPU

(2)单CPU的进程与抢占它的进程

(3)中断与进程之间

 

解决竞态途径:互斥访问

 

临界区:访问共享资源的代码区

 

互斥途径:中断屏蔽、原子操作、自旋锁、信号量、互斥体

 

中断屏蔽

local_irq_disable() /*屏蔽中断*/
... 

critical section /*临界区 */
... 

local_irq_enable() /*开中断 */

中断屏蔽只能禁止和使能本CPU内的中断,不能解决SMP多CPU的竞态问题,不推荐使用。

 

/*禁止中断的操作以外,保存目前 CPU 的中断位信息*/

local_irq_save(flags)

local_irq_restore(flags)

 

/*只禁止中断的底半部*/

local_bh_disable()

local_bh_enable()

 

原子操作

1.设置原子变量的值 

void atomic_set(atomic_t *v, int i); //设置原子变量的值为 i 

atomic_t v = ATOMIC_INIT(0); //定义原子变量 v 并初始化为 0 

 

2.获取原子变量的值 

atomic_read(atomic_t *v); //返回原子变量的值 

 

3.原子变量加/减 

void atomic_add(int i, atomic_t *v); //原子变量增加 i 

void atomic_sub(int i, atomic_t *v); //原子变量减少 i 

 

4.原子变量自增/自减 

void atomic_inc(atomic_t *v); //原子变量增加 1 

void atomic_dec(atomic_t *v); //原子变量减少 1 

 

5.操作并测试 

int atomic_inc_and_test(atomic_t *v); 

int atomic_dec_and_test(atomic_t *v); 

int atomic_sub_and_test(int i, atomic_t *v); 

上述操作对原子变量执行自增、自减和减操作后(注意没有加)测试其是否为 0,为 0 则返回 true,否则返回 false。 

 

6.操作并返回 

int atomic_add_return(int i, atomic_t *v); 

int atomic_sub_return(int i, atomic_t *v); 

int atomic_inc_return(atomic_t *v); 

int atomic_dec_return(atomic_t *v); 

上述操作对原子变量进行加/减和自增/自减操作,并返回新的值。

static atomic_t xxx_available = ATOMIC_INIT(1); /*定义原子变量,赋值为1*/     
static int xxx_open(struct inode *inode, struct file *filp) 
{ 
     ... 
    if (!atomic_dec_and_test(&xxx_available)) /*测试值为0则返回true否则为false*/
    { 
        atomic_inc(&xxx_available); 
        return  - EBUSY; /*已经打开*/ 
    } 
     ...   
    return 0; /* 成功 */ 
} 
static int xxx_release(struct inode *inode, struct file *filp) 
{ 
    atomic_inc(&xxx_available); /* 释放设备 */ 
    return 0; 
}

 

自旋锁

spinlock_t lock;   /*定义*/

spin_lock_init(&lock); /*初始化*/ 

spin_lock (&lock) ; /*获取自旋锁,保护临界区*/

....../*临界区*/  

spin_unlock (&lock) ; /*解锁*/ 

 1 int xxx_count = 0;/*定义文件是否被打开*/        
 2 static int xxx_open(struct inode *inode, struct file *filp) 
 3 { 
 4      ... 
 5     spinlock(&xxx_lock); 
 6     if (xxx_count)/*已经打开*/ 
 7     { 
 8         spin_unlock(&xxx_lock); 
 9         return  - EBUSY;  
10     } 
11     xxx_count++;/*增加使用计数*/ 
12     spin_unlock(&xxx_lock); 
13      ...   
14     return 0; /* 成功 */ 
15     }  
16 static int xxx_release(struct inode *inode, struct file *filp) 
17 { 
18     ... 
19     spinlock(&xxx_lock); 
20     xxx_count--; /*减少使用计数*/ 
21     spin_unlock(&xxx_lock); 
22     return 0; 
23 }

xxx_count为一个全局变量,当一个进程获得自旋锁,count++,其他进程获取自旋锁是count值不为0,return - EBUSY

 

自旋锁与中断屏蔽衍生:

spin_lock_irq() = spin_lock() + local_irq_disable() 

spin_unlock_irq() = spin_unlock() + local_irq_enable() 

spin_lock_irqsave() = spin_unlock() + local_irq_save() 

spin_unlock_irqrestore() = spin_unlock() + local_irq_restore() 

spin_lock_bh() = spin_lock() + local_bh_disable() 

spin_unlock_bh() = spin_unlock() + local_bh_enable()

 

信号量

DECLARE_MUTEX(mount_sem);//定义并初始化信号量

down(&mount_sem);//获取信号量,保护临界区   

...   

critical section //临界区   

...   

up(&mount_sem);//释放信号量  

 

初始化信号量的方法:

void sema_init (struct semaphore *sem, int val); //初始化信号量,并设置信号量 sem 的值为 val。尽管信号量可以被初始化为大于 1 的值从而成为一个计数信号量,但是它通常不被这样使用。 

void init_MUTEX(struct semaphore *sem); //初始化一个用于互斥的信号量,它把信号量 sem 的值设置为 1,等同于sema_init (struct semaphore *sem, 1)。 

void init_MUTEX_LOCKED (struct semaphore *sem); //初始化一个信号量,但它把信号量 sem 的值设置为 0,等同于sema_init (struct semaphore *sem, 0)。 

此外,下面两个宏是定义并初始化信号量的“快捷方式”。 

DECLARE_MUTEX(name) 

DECLARE_MUTEX_LOCKED(name) 

前者定义一个名为 name 的信号量并初始化为 1,后者定义一个名为 name 的信号量并初始化为 0。

 1 static DECLARE_MUTEX(xxx_lock);//定义互斥锁     
 2 static int xxx_open(struct inode *inode, struct file *filp)   
 3 {   
 4      ...   
 5     if (down_trylock(&xxx_lock))  //获得信号量返回值为0  
 6     return  - EBUSY;  //设备忙   
 7     ...     
 8     return 0; /* 成功 */   
 9 }    
10 static int xxx_release(struct inode *inode, struct file *filp)   
11 {   
12     up(&xxx_lock);  //释放打开锁   
13     return 0;   
14 }   

 

自旋锁和信号量

进程在获取不到自旋锁时会在一个循环中重复“测试并设置”操作,即“自旋”,进程在获取不到信号量时会进入休眠状态等待。

当进程占用资源时间较长采用信号量,临界区访问时间较短时用自旋锁。

 

 

 

posted @ 2015-09-07 19:10  Mage.Sun  阅读(307)  评论(0编辑  收藏  举报