一,linux内核中并发和竟态的解决方法
>竟态是怎么产生的?产生的根本原因是什么?
首先我们知道,在多个应用程序通过设备文件同时访问一个硬件资源的时候,
竟态就会产生了.
而产生竟态的根本原因就是(并发)总结归为三点↓
1> 在一个单核处理器上,内核支持抢占.
2> 在多核处理器上,内核与内核之间会产生竟态.
3> 中断和进程间会产生竟态(指的是支持嵌套的中断)
二,解决竟态的方法,
首先我们知道,产生竟态的原因是并发,所以我们只需要考虑让其变成互斥就可以解决这个问题.
所以在内核中我们可以让进程间实现互斥的办法有:中断屏蔽,自旋锁,信号量,互斥体,原子变量/操作.
这些解决竟态的方法有利有弊,各有所长.
1>中断屏蔽
特点:针对单核的cpu有效,中断屏蔽就是关闭中断.中断屏蔽保护的临界区很小,里面不可以有
延时或者耗时的操作(copy_to_user)如果中断屏蔽的时间很长,会导致用户的数据丢失或内核的崩溃.
API:
iocal_irq_disable();
//需要受到保护的临界区
iocal_irq_enable();
因为在开发的工作学习的过程中,我们接触单核处理器的可能性就没有,所以说这个中断屏蔽可能也不会用到.
2>自旋锁
特点:当一个进程获取到自旋锁后,另一个进程也想获取这把锁,此时后一个进程就处于一个自旋状态(消耗cpu资源)
自旋锁是针对于多核处理器设计的,自旋锁是一个忙等锁,自旋的状态会消耗cpu的咨询的资源,并且会导致死锁,自旋锁
保护的临界区要尽可能的短,里面不能有延时,耗时,休眠等操作,包括copy_from/to_user的操作,自旋锁在上锁的时候会
关闭抢占.
API:
1,spinlock_t lock;//定义一个自旋锁(全局)
2,spin_lock_init(&lock);//初始化一个自旋锁(初始化操作,毋庸置疑放在入口函数里)
3,spin_lock(&lock); //上锁操作
(中间是临界区,切记一定是很小的.所以我们通常来定义一个变量)
4,spin_unlock(&lock); //解锁操作
3>信号量
特点:当一个进程获取到信号量之后,另一个进程也想获取这个信号量,此时后一个进程处于一个休眠的状态.
信号量不会产生死锁,并且保护的临界区可以很大,里面可以有延时,耗时,甚至休眠的操作,信号量在等待获取锁的
过程是不消耗cpu资源的,信号量不会关闭抢占.由于自旋锁在使用的时候,要求临界区不能做休眠操作,但是在某些
场合需要在临界区做休眠操作,又要考虑竟态问题,此时可以使用信号量来保护临界区。
API:
struct semaphore sem;//定义一个信号量
void sema_init(struct semaphore *sem, int val)//初始化
当val初始化为1的时候,才具备互斥的效果。
当val初始化为0的时候,就是同步机制。
void down(struct semaphore *sem); //上锁操作
int down_trylock(struct semaphore *sem);//上锁操作
二选一即可(函数尝试获取信号量sem,成功或不成功获取信号量,
函数都将立即返回,而down()函数在不能成功获取时将进入睡眠状态而一直等待下去)
成功返回0,失败返回1,不会休眠
void up(struct semaphore *sem); //解锁操作
4 >互斥体
互斥体:当一个进程获取到互斥体之后,另外一个进程
也想获取这个互斥体,此时后一个进程处于休眠状态。互斥体不会产生死锁,
保护的临界区可以很大,里面可以有延时,耗时,甚至休眠的操作。互斥体在等待获取锁的过程是不消耗cpu资源
互斥体不会关闭抢占。在保护临界区较少的临界资源时,互斥体的效率高于信号量。
API:
struct mutex mutex;//定义锁
mutex_init(&mutex);//初始化锁
mutex_lock(struct mutex *lock);//上锁
int mutex_trylock(struct mutex *lock)
成功返回1,失败返回0,不会休眠
void mutex_unlock(struct mutex *lock)//解锁
5>原子操作
typedef struct {
int counter;
} atomic_t;
原子操作本身就是一个变量,这个变量的修改内核做了
防竞态的过程,对这个变量值修改的过程是通过内联汇编
完成。意思就是对这个变量的操作看成一个不可被分割的
整体。
atomic_t atm=ATOMIC_INIT(-1);
atomic_inc_and_test(atomic_t *v)
加1后和0比较,如果结果为0,表示获取锁成功了,
获取锁成功返回真
atomic_dec(atomic_t *v)
| |
| |
↑
二选一
↓
| |
| |
atomic_t atm=ATOMIC_INIT(1);
atomic_dec_and_test(atomic_t *v)
减1后和0比较,如果结果为0,表示获取锁成功了,
获取锁成功返回真
atomic_inc(atomic_t *v)