{//?什么是并发,为何要使用并发机制
并发即是 多件事情同时在执行
只有使这些设备都并发的执行才能满足性能要求。
例:
如果系统上挂10个设备,每个设备都请求,如果是串行顺序执行,
可能一个设备要等较长时间,系统才能响应它
}
{//?如何实现并发
CPU是顺序的读入指令执行的, 如何来实现并发呢?
例:
当你A先生的业务时,B女士来个电话,你接完电话后继续做A的事。
如何你切换足够快,即在A,B的忍耐时间内。 感觉就像在并发执行一样。
使用中断的方式可实现并发 //中断: 类似于接电话,打断当前执行,等处理完后,返回再接着执行。
}
{//?为什么要进行并发控制
并发会导致竞态的发生。 //竞态: 一个资源被并发执行的任务同时访问时,就会出现竞争。此时的状态就是竞态。
需保证一个资源在一个时间只能被一个任务访问,才能避免混乱。即并发控制
}
{//?如何进行并发控制
采用互斥机制对并发进行控制 //互斥: 共享资源被某任务访问时,别的任务禁止访问。
互斥机制:
{//中断屏蔽
使用禁止中断方式,避免中断的影响
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
}
{//原子操作
并发中不被打断的最小单元
{//---hello_atomic/hello.c 实现设备只被一个进程打开
#include <asm/atomic.h>
static atomic_t hello_atomic = ATOMIC_INIT(1); //定义原子变量hello_atomic ,并初始化为1
static int hello_open(struct inode *inode,struct file *file)
{
if (!atomic_dec_and_test(&hello_atomic)) { // atomic_dec_and_test表示原子变量自减一,并测试是否为了零,如果为零返回真
//当已open过,还未release时,再次open , hello_atomic为0, 再减一不为零,!atomic_dec_and_test 成立
atomic_inc(&hello_atomic); //原子变量加一,恢复自减前状态
return - EBUSY; //已经被打开
}
//当第一次被open时, hello_atomic为1 , !atomic_dec_and_test 不成立, 正常打开
printk("hello open \n");
return 0;
}
static int hello_release(struct inode *inode,struct file *file)
{
atomic_inc(&hello_atomic); //释放设备
printk("hello release\n");
return 0;
}
}
}
{//自旋锁 spinlock
死循环空转CPU 等待释放锁, 不睡眠, 适用于锁持有时间小于睡眠唤醒时间场合
{//---hello_spinlock/hello.c
static spinlock_t hello_spinlock;
static int hello_resource = 1;
static int hello_open(struct inode *inode,struct file *file)
{
spin_lock(&hello_spinlock); //还可和中断屏蔽合用 spin_lock_irq
if(hello_resource == 0)
{
spin_unlock(&hello_spinlock);
return - EBUSY;
}
hello_resource--;
spin_unlock(&hello_spinlock);
printk("hello open \n");
return 0;
}
static int hello_release(struct inode *inode,struct file *file)
{
spin_lock(&hello_spinlock);
hello_resource++;
spin_unlock(&hello_spinlock);
printk("hello release\n");
return 0;
}
}
{//读写锁 rwlock_t
它是从资源访问的特性上,对锁进行了优化, 读可同时进行 ,读写互斥
}
{//顺序锁 seqlock
它是对读写锁的进一步优化,放开了读写互斥, 即仅写间互斥。即使读者正在读的时候也允许写者继续运行
}
{//无锁设计
如环形缓冲 类似生产者和消费者模型
}
//加锁粒度
}
{//信号量
进程当中用于并发互斥和,资源的计数。
相对于自旋锁,信号量会睡眠,仅能用于进程中
//---hello_semaphore/hello.c
#include <linux/semaphore.h>
static DECLARE_MUTEX(hello_semlock); //定义一个初始值为一的信号量
static int hello_open(struct inode *inode,struct file *file)
{
if (down(&hello_semlock)) /* 获得打开锁*/
{
return - EBUSY; /* 设备忙*/
}
printk("hello open \n");
return 0;
}
static int hello_release(struct inode *inode,struct file *file)
{
up(&hello_semlock); /* 释放打开锁*/
printk("hello release\n");
return 0;
}
}
{//互斥体
互斥体是专门用来做互斥的, 和二元的信号量类似,
struct mutex my_mutex; /* 定义mutex */
mutex_init(&my_mutex); /* 初始化mutex */
mutex_lock(&my_mutex); /* 获取mutex */
.../* 临界资源*/
mutex_unlock(&my_mutex); /* 释放mutex */
}
}
{//?并发机制使用场合
1. 中断屏蔽的使用场合
当有中断处理程序访问共享资源的时候。
2. 原子操作的使用场合
只使用于共享资源为一个变量的操作的情况
3. 自旋锁的使用场合
在临界区代码运行时间比较短的情况。
多CPU的情况下
4. 信号量的使用场合
临界区代码运行时间比较长的情况。
当锁持有的时间不是很长的时候,优先使用信号量。
1注意: 中断里不能使用信号量。 因为中断不能睡眠。
}
}
linux设备驱动中的并发控制,布布扣,bubuko.com
linux设备驱动中的并发控制