因为工作原因,想系统的学习下驱动,参考韦东山嵌入式视频,做一些笔记
1 需要同步互斥的原因
2 内核中实现同步互斥的常用方法
2.1 原子操作
2.1.1 实现方式
在原子操作中,架构以ARMv6位分界点,实现方式有所不同
架构 | 实现 |
---|
<ARMv6(不支持SMP) | 1 关中断 2 操作变量 3 恢复中断 |
>=ARMv6 | 使用strex和sdrex多次尝试 |
函数名 | 作用 |
---|
atomic_read(v) | 读出原子变量的值,即v->counter |
atomic_set(v,i) | 设置原子变量的值,即v->counter = i |
atomic_inc(v) | v->counter++ |
atomic_dec(v) | v->counter– |
atomic_add(i,v) | v->counter += i |
atomic_sub(i,v) | v->counter -= i |
atomic_inc_and_test(v) | 先加1,再判断新值是否等于0;等于0的话,返回值为1 |
atomic_dec_and_test(v) | 先减1,再判断新值是否等于0;等于0的话,返回值为1 |
2.2 原子位操作
下表中p是一个unsigned long指针
函数名 | 作用 |
---|
set_bit(nr,p) | 设置(*p)的bit nr为1 |
clear_bit(nr,p) | 清除(*p)的bit nr为0 |
change_bit(nr,p) | 改变(*p)的bit nr,从1变为0,或是从0变为1 |
test_and_set_bit(nr,p) | 设置(*p)的bit nr为1,返回该位的老值 |
test_and_clear_bit(nr,p) | 清除(*p)的bit nr为0,返回该位的老值 |
test_and_change_bit(nr,p) | 改变(*p)的bit nr,从1变为0,或是从0变为1;返回该位的老值 |
2.3 锁
linux中常见的锁的分类
2.2.1 自旋锁
具体实现
简单地说就是无法获得锁时,不会休眠,会一直循环等待
CPU | 实现 |
---|
多CPU | 使用原子操作实现 |
单CPU | 禁止别的进程调度(禁止抢占) |
函数名 | 作用 |
---|
spin_lock_init(_lock) | 初始化自旋锁为unlock状态 |
void spin_lock(spinlock_t *lock) | 获取自旋锁(加锁),返回后肯定获得了锁 |
int spin_trylock(spinlock_t *lock) | 尝试获得自旋锁,成功获得锁则返回1,否则返回0 |
void spin_unlock(spinlock_t *lock) | 释放自旋锁,或称解锁 |
int spin_is_locked(spinlock_t *lock) | 返回自旋锁的状态,已加锁返回1,否则返回0 |
2.4 信号量
函数名 | 作用 |
---|
DEFINE_SEMAPHORE(name) | 定义一个struct semaphore name结构体,count值设置为1 |
void sema_init(struct semaphore *sem, int val) | 初始化semaphore |
void down(struct semaphore *sem) | 获得信号量,如果暂时无法获得就会休眠,返回之后就表示肯定获得了信号量,在休眠过程中无法被唤醒,即使有信号发给这个进程也不处理 |
int down_interruptible(struct semaphore *sem) | 获得信号量,如果暂时无法获得就会休眠,休眠过程有可能收到信号而被唤醒,要判断返回值:0:获得了信号量 -EINTR:被信号打断 |
int down_killable(struct semaphore *sem) | 跟down_interruptible类似,down_interruptible可以被任意信号唤醒,但down_killable只能被“fatal signal”唤醒,返回值:0:获得了信量EINTR:被信号打断 |
int down_trylock(struct semaphore *sem) | 尝试获得信号量,不会休眠,返回值:0:获得了信号量1:没能获得信号量 |
int down_timeout(struct semaphore *sem, long jiffies) | 获得信号量,如果不成功,休眠一段时间返回值:0:获得了信号量-ETIME:这段时间内没能获取信号量,超时返回down_timeout休眠过程中,它不会被信号唤醒 |
void up(struct semaphore *sem) | 释放信号量,唤醒其他等待信号量的进程 |
2.5 互斥量/互斥体
函数名 | 作用 |
---|
mutex_init(mutex) | 初始化一个struct mutex指针 DEFINE_MUTEX(mutexname) 初始化struct mutex mutexname |
int mutex_is_locked(struct mutex *lock) | 判断mutex的状态1:被锁了(locked)0:没有被锁 |
void mutex_lock(struct mutex *lock) | 获得mutex,如果暂时无法获得,休眠返回之时必定是已经获得了mutex |
int mutex_lock_interruptible(struct mutex *lock) | 获得mutex,如果暂时无法获得,休眠;休眠过程中可以被信号唤醒,返回值:0:成功获得了mutex-EINTR:被信号唤醒了 |
int mutex_lock_killable(struct mutex *lock) | 跟mutex_lock_interruptible类似,mutex_lock_interruptible可以被任意信号唤醒,但mutex_lock_killable只能被“fatal signal”唤醒,返回值:0:获得了mutex-EINTR:被信号打断 |
int mutex_trylock(struct mutex *lock) | 尝试获取mutex,如果无法获得,不会休眠,返回值:1:获得了mutex,0:没有获得注意,这个返回值含义跟一般的mutex函数相反, |
void mutex_unlock(struct mutex *lock) | 释放mutex,会唤醒其他等待同一个mutex的线程 |
int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock) | 让原子变量的值减1,如果减1后等于0,则获取mutex,返回值:1:原子变量等于0并且获得了mutex0:原子变量减1后并不等于0,没有获得mutex |
3 何时用那种锁
举例简单介绍一下,上表中第一行“IRQ Handler A”和第一列“Softirq A”的交叉点是“spin_lock_irq()”,意思就是说如果“IRQ Handler A”和“Softirq A”要竞争临界资源,那么需要使用“spin_lock_irq()”函数。为什么不能用spin_lock而要用spin_lock_irq?也就是为什么要把中断给关掉?假设在Softirq A中获得了临界资源,这时发生了IRQ A中断,IRQ Handler A去尝试获得自旋锁,这就会导致死锁:所以需要关中断。