Linux设备驱动中的并发控制,原子打操作、自旋锁、信号量、完成量、互斥体

时间:2022-01-16 15:11:36
linux设备驱动中的并发控制
7.2中断屏蔽
中断屏蔽的使用方法为:
local_irq_disable(); //开中断
....
critical section //临界区
......
local_irq_enable(); //关中断
以上两个只能对本CPU内的中断起作用,SMP多CPU并不能解决竞态
local_bh_disable()//关中断低半部
local_bh_enable()//开中断低半部


local_irq_save();//关中断并保存状态字
local_irq_restore();//开中断并恢复状态








7.3.1原子打操作
1.整型原子操作
atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0
void atomic_set(atomic_t *v, int i); //设置原子变量的值为i




2.获取原子变量的值
atomic_read(atomic_t *v) //返回原子变量的值


3.原子变量的加、减
void atomic_add(int i, atomic_t *v); //原子变量增加i
void atomic_sub(int i, atomic_t *v); //原子变量减少i


4.原子变量的自增、自减
void atomic_inc(atomic_t *v); //原子变量增加1
void atomic_dec(atomic_t *v); //原子变量减少1


5.操作并测试
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);


6.操作并返回
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);


7.3.2位原子操作
1.设置位
void set_bit(nr, void *addr); //置位addr地址的第nr位,即对应位写为1。


2.清除位
void clear_bit(nr, void *addr);//清除addr地址的第nr位,即对应位写0。


3.改变位
void change_bit(nr, void *addr); //对addr第nr位进行反置, 即取反操作。


4.测试位
test_bit(nr, void*addr); //返回addr地址的第nr位的值。


5.测试并操作位
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);




应用实例:
atomic_t enable;


atomic_set(&enable, 0);


atomic_read(&enable);




7.4.1自旋锁
linux系统中与自旋锁相关的操作主要有如下4种:
1.定义自旋锁
spinlock_t spin;


2.初始化自旋锁
spin_lock_init(lock); //该宏用于动态初始化自旋锁lock


3.获得自旋锁
spin_lock(lock); //该宏用于获得自旋锁lock,如果能够立即获得锁,它就马上返回,否则,它将自旋在那里,
直到该自旋锁的保持者释放;
spin_trylock(lock);//该宏用于尝试获得自旋锁lock,如果能够立即获得锁,它获得锁并返回真,否则立即返回
假,实际上不再“原地打转”;


4.释放自旋锁
spin_unlock(lock);//该宏用于释放自旋锁lock,它与spin_lock或spin_trylock配对使用。
自旋锁一般这样被使用:
spinlock_t lock;
spin_lock_init(&lock);


spin_lock(&lock); //获得自旋锁,保护临界区
.....//临界区
spin_unlock(&lock); //解锁


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()






7.5.1  信号量的使用
信号量只有得到信号量的进程才能执行临界区代码,但是与自旋锁不同的是,当获取不到信号量时,
进程不会原地打转而是进入休眠等待状态。
1.定义信号量
struct semaphore sem;


2.初始化信号量
void sema_init(struct semaphore *sem, int val);


void init_MUTEX(struct semaphore *sem);  // sem 1
void init_MUTEX_LOCKED(struct semaphore *sem); // sem 0


DECLARE_MUTEX(name)
DECLARE_MUTEX_LOCKED(name)


3.获得信号量
void down(struct semaphore *sem); //该函数用于获得信号量sem 它会导至处睡眠,所以不能在上下文中使用


int down_interruptible(struct semaphore*sem);


int down_trylock(struct semaphore *sem); //可以在上下文中使用


4.释放信号量
void up(struct semaphore *sem);


信号量的使用:
DECLARE_MUTEX(mount_sem);
down(&mount_sem); //获得信号量,保护临界区
....
    临界区
....
up(&mount_sem);//释放信号量




例:
struct semaphore sem;


int xxx_init(void)
{
...
init_MUTEX(&sem);
...
}


int xxx_open(struct inode *inode, struct file *file)
{
....
down(&sem);
/*临界资源*/
....
return 0
}


int xxx_release(struct inode *inode, struct file *file)
{
...
up(&sem); //
...
}


7.5.3完成量用于同步
1.定义完成量
struct completion my_completion;


2.初始化completion
init_completion(&my_completion);
DECLARE_COMPLETION(my_completion);


3.等待完成量
void wait_for_completion(struct completion *c);


4.唤醒完成量
void complete(struct completion *c);
void complete_all(strcut completion *c);




执行单元A                               执行单元B
struct completion com;
init_completion(&com);


代码区域a                      激活      代码区域c
wait_for_completion(&sem); <------------complete(&com);
.....
......;
代码区域b








struct completion ps_comple;


//in probe
init_completion(&ps_comple);


//定义并初始化completion 等同于做了上两行代码的效果
//DECLARE_COMPLETION(completion);




//in resume
INIT_COMPLETION(ps_comple); //重新初始化completion


//write
complete(&ps_comple);


//read
if(wait_for_completion(&ps_comple))


//read 可中断的 等待完成量
if(wait_for_completion_interruptible(&ts->fw_completion))


wait_for_completion_interruptible_timeout


wait_for_completion_timeout






7.6互斥体
定义互斥体并初始化
struct mutex my_mutex;
mutex_init(&my_mutex);


获取互斥体
void fastcall mutex_lock(strcut mutex *lock);
int fastcall mutex_lock_interruptible(struct mutex *lock);
int fastcall mutex_trylock(struct mutex *lock);


释放互斥体
void fastcall mutex_unlock(struct mutex *lock);


mutex 的使用方法和信号量用于互斥的场合完全一样,
struct mutex my_mutex;
mutex_init(&my_mutex);


mutex_lock(&my_mutex);
....//临界资源
mutex_unlock(&my_mutex);


mutex_destroy(&my_mutex);