1. 内核的并发和竞态
1.1 什么时候出现竞态
当同一个驱动程序,同时被多个应用层程序访问,驱动程序就会产生竞态。
1.2 竞态产生的根本原因
1. 在单核cpu中,如果内核支持抢占(高优先级中断可以打断低优先级中断),就会产生竞态。
2. 在多核cpu中,核与核之间会产生竞态
3. 在中断和进程之间会产生竞态(中断优先级高于进程优先级)
4. 中断和中断之间会产生竞态(中断需要支持嵌套)
1.3 解决竞态的方法
顺序执行
互斥执行
2. 互斥执行
2.1 中断屏蔽
针对单核cpu,取消中断
2.2 自旋锁
原理
当一个进程获取到自旋锁后,另一个进程也想获取自旋锁,此时后一个进程进入自旋状态。
特点
自旋锁针对于多核cpu设计
自旋锁需要消耗cpu资源
自旋锁保护的临界资源比较小,保护的临界资源中不可以出现copy_to_user和copy_from_user函数
自旋锁会产生死锁(锁的是当前的处理器)
自旋锁上锁的时候会关闭抢占
自旋锁工作于进程上下文,工作于中断上下文
API接口
定义一把锁 spinlock_t spinlock;
初始化锁 void spin_lock_init(spinlock_t *lock)
上锁 void spin_lock(spinlock_t *lock)
解锁 void spin_unlock(spinlock_t *lock)
2.3 信号量
原理
当一个进程获取到信号量之后,另一个进程也想获取信号量,此时后一个进程进入休眠状态。
特点
信号量针对于多核cpu设计
信号量不消耗cpu资源
信号量保护的临界资源比较大,并且在临界资源中可以出现延时、耗时甚至休眠的操作
保护的临界资源中可以出现copy_to_user和copy_from_user函数
信号量不会产生死锁
信号量上锁的时候不会关闭抢占
信号量工作于进程上下文
API接口
定义信号量 struct semaphore sema;
信号量初始化 void sema_init(struct semaphore *sem, int val)
val = 0 表示同步执行 顺序执行
val = 1 表示互斥执行
上锁 void down(struct semaphore *sem);
解锁 void up(struct semaphore *sem);
尝试获取锁 int down_trylock(struct semaphore *sem);
返回0,获取信号量成功
返回1,获取信号量失败
2.4 互斥体
原理
当一个进程获取到互斥体之后,另外一个进程也想获取互斥体,此时后一个进程会稍微等一会进入休眠状态,对于保护临界资源比较小的时候,互斥体的效率高于信号量
特点
互斥体针对于多核cpu设计
互斥体不消耗cpu资源
互斥体保护的临界资源比较大,并且在临界资源中可以出现延时、耗时甚至休眠的操作
保护的临界资源中可以出现copy_to_user和copy_from_user函数
互斥体不会产生死锁
互斥体上锁的时候不会关闭抢占
互斥体工作于进程上下文
API接口
定义互斥体 struct mutex mutex;
初始化互斥体 mutex_init(&mutex);
上锁 void mutex_lock(struct mutex *lock);
解锁 void mutex_unlock(struct mutex *lock);
尝试获取互斥体 int mutex_trylock(struct mutex *lock)
成功返回1
失败返回0
2.5 原子变量
原理
原子变量本身就是一个变量,没有自旋或者休眠的状态
获取到原子变量成功,获取不到失败
对于原子变量在内核中通过内联汇编实现的,省去中间环节
对于原子变量是一个不可分割的整体,所有叫做原子变量
API接口
定义并初始化原子变量: atomic_t atomic = ATOMIC_INIT(1);
上锁 =======> 减1
int atomic_dec_and_test(atomic_t *v)
减1之后和0值进行比较
和0值相等,上锁成功
和0值不相等,上锁失败
成功返回1,失败返回0
解锁 =======> 加1
void atomic_inc(atomic_t *v)
定义并初始化原子变量: atomic_t atomic = ATOMIC_INIT(-1);
上锁 =======> 加1
bool atomic_inc_and_test(atomic_t *v)
加1之后和0值进行比较
和0值相等,上锁成功
和0值不相等,上锁失败
成功返回1,失败返回0
解锁 =======> 减1
void atomic_dec(atomic_t *v)