1.并发控制介绍
- 并发会导致竞态的发生。 竞态: 一个资源被并发执行的任务同时访问时,就会出现竞争。此时的状态就是竞态。
- 需保证一个资源在一个时间只能被一个任务访问,才能避免混乱。即并发控制
2.并发控制机制
2.1 互斥体
互斥体是专门用来做互斥的, 和二元的信号量类似,
static struct mutex lock; /* 定义mutex */
mutex_init(& lock); /* 初始化mutex */
mutex_lock(& lock); /* 获取mutex */
.../* 临界资源*/
mutex_unlock(& lock); /* 释放mutex */
2.1 信号量
进程当中用于并发互斥和,资源的计数。
相对于自旋锁,信号量会睡眠,仅能用于进程中
#include <linux/semaphore.h>
static DEFINE_SEMAPHORE(demo_semlock); //定义一个初始值为一的信号量
static int demo_open(struct inode *inode,struct file *file)
{
if (down(&demo_semlock)) /* 获得打开锁*/
{
return - EBUSY; /* 设备忙*/
}
printk("demo open \n");
return 0;
}
static int demo_release(struct inode *inode,struct file *file)
{
up(&demo_semlock); /* 释放打开锁*/
printk("demo release\n");
return 0;
}
}
2.3 自旋锁
-
cpu自旋死循环空转CPU 等待释放锁, 耗资源, 适用于锁持有时间小于睡眠唤醒时间场合
-
进程递归获得锁时,会导致死锁
static DEFINE_SPINLOCK(demo_spinlock); static int hello_resource = 1; static int demo_open(struct inode *inode,struct file *file) { spin_lock(&demo_spinlock); //还可和中断屏蔽合用 spin_lock_irq if(demo_resource == 0) //为避免死机, spin_try_lock { spin_unlock(&demo_spinlock); return - EBUSY; } demo_resource--; spin_unlock(&demo_spinlock); printk("demo open \n"); return 0; } static int demo_release(struct inode *inode,struct file *file) { spin_lock(&hello_spinlock); demo_resource++; spin_unlock(&demo_spinlock); printk("demo release\n"); return 0; }
2.4 原子操作
并发中不被打断的最小单元
#include <asm/atomic.h>
static atomic_t demo_atomic = ATOMIC_INIT(1); //定义原子变量demo_atomic ,并初始化为1
static int demo_open(struct inode *inode,struct file *file)
{
/*原子变量减1,并测试是否为零,为零返回真*/
if (!atomic_dec_and_test(&demo_atomic)) {
atomic_inc(&demo_atomic); //原子变量加一,恢复自减前状态
return - EBUSY; //已经被打开
}
/*当第一次被open时, demo_atomic为1 , !atomic_dec_and_test 不成立, 正常打开*/
printk("demo open \n");
return 0;
}
static int demo_release(struct inode *inode,struct file *file)
{
atomic_inc(&demo_atomic); //释放设备
printk("demo release\n");
return 0;
}
2.5 中断屏蔽
使用禁止中断方式,避免中断的影响
local_irq_disable //屏蔽中断
临界区代码 //临界区: 访问共享资源的代码区域
local_irq_enable //开中断
注:
1。屏蔽中断的时间尽量短,否则会造成别的中断延时过久,甚至丢失,最好能采有屏蔽指定中断的方式
disable_irq(int irq); //屏蔽指定中断号的中断
enable_irq(int irq); //使能某中断号的中断
2。常需保存当前的中断状态,便于恢复
用local_irq_save(flags); 代替local_irq_disable
用local_irq_restore(flags) 代替 local_irq_enable
3 并发控制适用场合
- 中断屏蔽的使用场合(中断和中断间,进程和中断竞争)
当有中断处理程序访问共享资源的时候。 - 原子操作的使用场合(进程间,中断间综合竞争。信号量、中断屏蔽等都是基于原子操作来实现)
只使用于共享资源为一个变量的操作的情况 - 自旋锁的使用场合(多CPU 竞争)
多CPU的情况下
在临界区代码运行时间比较短的情况。 - 信号量互斥体的使用场合(进程间竞争,它不能防止中断的打断,故要防止中断打断,需与中断屏蔽混合使用)
临界区代码运行时间比较长的情况。
当锁持有的时间较长的时候,优先使用信号量。
注意: 中断里不能使用信号量。 因为中断内不能睡眠。