7.1并发与竞态
(1)、竞态的发生场景:CPU0的进程与CPU1的进程之间、CPU0的中断与CPU1的进程之间、CPU0的中断与CPU1的中断之间;
(2)、解决竞态问题的途径是保证对共享资源的互斥访问,所谓互斥访问是指一个执行单元在访问共享资源的时候,其他的执行单元被禁止访问,访问共享资源的代码区域被称为临界区,临界区需要被以某种互斥机制加以保护。中断屏蔽、原子操作、自旋锁、信号量、互斥体等是Linux设备驱动中可采用的互斥途径;
7.2编译乱序与执行乱序
(1)、编译乱序:打开编译器优化后,生成的代码并没有严格按照代码逻辑顺序执行;
解决方法:在代码中设置barrier()编译屏障
(2)、执行乱序:处理器执行顺序不同;
解决方法:在代码中设置内存屏障;Linux内核的自旋锁、互斥体等互斥逻辑,需要用到上述 逻辑,在请求获得锁时,调用屏障指令,解锁时,也需要调用屏障指令
7.3中断屏蔽
(1)、中断屏蔽的使用方法为:
local_irq_disable() /*屏蔽中断*/
critical section /*临界区*/
local_irq_enable() /*开中断*/
(2)、 local_irq_disable()与local_irq_enable()都只能禁止和使能本CPU内的中断,因此并不能解决SMP多CPU引发的竞态,使用上述接口意味着bug,它适合于自旋锁联合使用;
(3)、 local_irq_save(flags)除了进行禁止中断的操作以外,还保存目前CPU的中断位信息, local_irq_restore(flags)与之相反,对于ARM而言,就是保存和恢复CPSR;
(4)、如果只是禁止中断的底半部,应使用local_bh_disable()与local_bh_enable()
7.4原子操作
(1)、原子操作可以保证对一个整型数据的修改是排他性的,ldrex指令跟strex配对使用,可以让总线监控ldrex到strex之间有无其他的实体存取该地址;
(2)、设置原子变量的值
void atomic_set(atomic_t *v,int i); /*设置原子变量的值为i*/
atomic_t v = ATOMIC_INIT(0); /*定义原子变量v并初始化为0*/
(3)、获取原子变量的值
atomic_read(atomic_t *v); /*返回原子变量的值*/
(4)、原子变量加减
void atomic_add(int i ,atomic_t *v); /*原子变量增加i*/
void atomic_sub(int i ,atomic_t *v); /*原子变量减少i*/
(5)、原子变量自增/自减
void atomic_inc(atomic_t *v); /*原子变量增加1*/
void atomic_dec(atomic_t *v); /*原子变量减少1*/
(6)、操作并测试
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i ,atomic_t *v);
上述操作对原子变量执行自增、自减和减操作后(注意没有加),测试其是否为0,为0返回
true,否则返回false;
(7)、操作并返回
int atomic_add_return(int i ,atomic_t *v);
int atomic_dec_return(int i ,atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
上述操作对原子变量进行加/减和自增/自减操作,并返回新的值;
7.5自旋锁
(1)、自旋锁:是一种典型的 对临界资源进行互斥访问的手段,其名称来源于它的工作方式。为了获得一个自旋锁,在某CPU上运行的代码需要先执行一个原子操作,该操作测试并设置某个内存变量。由于它是原子操作,所以在该操作完成之前其他执行单元不能访问这个内存变量。如果测试结果表明锁已经空闲,则程序获得这个锁并继续执行;如果测试结果表明锁仍被占用,程序将在一个小的循环内重复这个"测试并设置"操作,即进行所谓的“自旋”;
(2)、定义自旋锁
spinlock_t lock;
(3)、初始化自旋锁
spin_lock_init(&lock);
(4)、获得自旋锁
spin_lock(&lock);
该宏用于获得自旋锁lock,如果能够立即获得锁,它就马上返回,否则它将在那里自旋,直到该自旋锁的保持者释放。
spin_trylock(&lock);
该宏尝试获得自旋锁lock,如果能立即获得锁,它获得锁并返回true,否则立即返回false,实际上不再原地打转。
(5)、释放自旋锁
spin_unlock(&lock);
(6)、自旋锁的使用
spinlock_t lock;
spin_lock_init(&lock);
spin_lock(&lock);
/*临界区*/
spin_unlock(&lock);
(7)、尽管用了自旋锁可以临界不受到别的CPU和本CPU内的抢占进程打扰,但是得到锁的代码路径在执行临界区的时候,还可能受到中断和底半部的影响。为了防止这种影响,就需要用到自旋锁的衍生:
spin_lock_irq() = spin_lock() + local_irq_disable()
spin_unlock_irq() = spin_unlock() + local_irq_enable()
spin_lock_irqsave() = spin_lock() + local_irq_save()
spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()
spin_lock_bh() = spin_lock() + local_bh_disable()
spin_unlock_bh() = spin_unlock() + local_bh_enable()
(8)、在多核编程的时候,如果进程和中断可能访问同一片临界资源,我们需要在进程上下文中调用
spin_lock_irqsave() = spin_lock() + local_irq_save()
spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()
在中断上下文调用
spin_lock(&lock)
spin_unlock(&lock);
7.6读写自旋锁
(1)、定义和初始化读写自旋锁
rwlock_t my_rwlock;
rwlock_init(&my_rwlock);
(2)、读锁定
void read_lock(rwlock_t *lock)
void read_lock_irqsave(rwlock_t *lock,unsigned long flags)
void read_lock_irq(rwlock_t *lock)
void read_lock_bh(rwlock_t *lock)
(3)、读解锁
void read_unlock(rwlock_t *lock)
void read_unlock_irqsave(rwlock_t *lock,unsigned long flags)
void read_unlock_irq(rwlock_t *lock)
void read_unlock_bh(rwlock_t *lock)
(4)、写锁定
void write_lock(rwlock_t *lock)
void write_lock_irqsave(rwlock_t *lock,unsigned long flags)
void write_lock_irq(rwlock_t *lock)
void write_lock_bh(rwlock_t *lock)
(6)、写解锁
void write_unlock(rwlock_t *lock)
void write_unlock_irqsave(rwlock_t *lock,unsigned long flags)
void write_unlock_irq(rwlock_t *lock)
void write_unlock_bh(rwlock_t *lock)
(7)、读写锁使用
rwlock_t lock
rwlock_init(&lock)
read_lock(&lock)
......
read_unlock(&lock)
write_lock_irqsave(&lock,flags)
......
write_unlock_irqrestore(&lock,flags)
7.7信号量
(1)、信号量与操作系统的经典概念PV操作对应
P(S):1、如果信号量s的值大于零,该进程继续执行
2、如果s的值为0,该进程置为等待状态,排入信号量的等待队列,直到V操作唤醒
(2)、定义信号量
struct semaphore sem;
(3)、初始化信号量
void sema_init(struct semaphore *sem,int val)
(4)、获得信号量
void down(struct semaphore *sem)该函数会导致睡眠,因此不能在中断上下文中使用
void down_interruptible(struct semaphore *sem)
void down_trylock(struct semaphore *sem)
(5)、释放信号量
void up(struct semaphore *sem)
7.8互斥体
(1)、struct mutex my_mutex
mutex_init(&my_mutex)
void mutex_lock(struct mutex *lock)
int mutex_lock_interruptible(struct mutex *lock)
int mutex_trylock(struct mutex *lock)
void mutex_unlock(struct mutex *lock)