一、概述
并发:多个执行单元同时或并行运行
竞争:多个执行单元同时并行的访问某个共享资源,导致竞争,竟态。
同步:多个执行单元协调动作,相互配合,串行的共同完成一个任务。
发生竟态时,可以使用以下方法对临界资源做同步处理:
- 原子量(atomic)
用于临界资源数量控制 - 自旋锁(spin_lock)
进程上下文和中断上下文都能使用,CPU 获取不到自旋锁,会忙等待.
代码获取自旋锁后:
1、不受别的CPU和本CPU的进程抢占打扰,但可以被中断和底半部抢占
2、进行调度,抢占以及在等待队列、互斥锁、信号量上睡眠都是非法的 - 互斥锁(mutex)
互斥锁用来保证在任一时刻只能有一个线程访问或执行临界区,只能用于进程上下文 - 信号量(semphore)
信号量是用于保护临界区的一种常用方法,只能进程上下文获取信号量。
信号量只能用于进程上下文,不能用于中断上下文,也不能持有自旋锁情况下使用信号量。
二、函数接口
1. 原子量
atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0
atomic_read(atomic_t *v); //返回原子变量的值
void atomic_inc(atomic_t *v); //原子变量增加1
void atomic_dec(atomic_t *v); //原子变量减少1
int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。
2. 自旋锁
spin_lock_init();
spin_lock
spin_unlock
spin_lock_irq 禁止中断
spin_unlock_irq 恢复中断
spin_lock_irqsave
spin_unlock_irqrestore
spin_lock_bh = spin_lock + local_bh_disable 关底半部
spin_unlock_bh = spin_unlock + local_bh_enable 开底半部
3. 互斥锁
struct mutex my_mutex;
mutex_init(&my_mutex);
DEFINE_MUTEX(mutexname) //以上两句的合二为一
mutex_lock()
mutex_lock_interruptible() ///可被中断打断
mutex_lock_killable() ///可被信号打断
mutex_trylock() //返回1,代码获取互斥锁成功
mutex_destroy
mutex_unlock
3. 信号量
struct semaphore sem;
void sema_init(struct semaphore *sem, int val)
DEFINE_SEMAPHORE(name) //信号量资源值初始化为1 ,此时与互斥锁类似
void down(struct semaphore *sem); //获取信号量,资源值减1
void down_interruptible(struct semephore *sem)//获取信号量,资源值减1,睡眠后可被信号中断
void down_trylock(struct semaphore *sem);//不会导致调用者睡眠,可以在中断上下文使用,返回0 获取信号量成功
void down_timeout(struct semaphore *sem, long jiffies) // 超时,返回
int down_killable(struct semaphore *sem) //
void up(struct semaphore *sem);//释放信号量,资源值加1
三、代码示例
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/semaphore.h>
#define hp_prt(fmt, arg...) printk("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg);
spinlock_t hp_lock;
struct semaphore hp_sem;
DEFINE_MUTEX(hp_mutex);
void timer_func(struct timer_list * timelist)
{
hp_prt("enter\n");
/* 中断上下文与进程上下文使用同一个自旋锁时,应使用spin_lock_irq.进程上下文拿锁后被中断再拿锁可能导致死锁 */
spin_lock(&hp_lock);
hp_prt("spin_lock get enter\n");
spin_unlock(&hp_lock);
mdelay(30000);// 中断中可以使用忙等延时,不能使用睡眠。
hp_prt("up semaphore!\n");
up(&hp_sem); // 释放信号量 code_case_concurrent_init执行完成
}
DEFINE_TIMER(hp_timer,timer_func);
static int code_case_concurrent_init(void)
{
int r;
static atomic_t canopen = ATOMIC_INIT(1);
atomic_inc(&canopen);
r = atomic_read(&canopen);
hp_prt("atomic_read:%d\n", r);
atomic_dec(&canopen);
r = atomic_read(&canopen);
hp_prt("atomic_read:%d\n", r);
if(atomic_dec_and_test(&canopen)){
hp_prt("atomic_dec_and_test == 0\n");
}
sema_init(&hp_sem, 1); //信号量资源初始化为1
spin_lock_init(&hp_lock);
spin_lock(&hp_lock);
hp_prt("spin_lock get!\n");
hp_timer.expires = jiffies + 1 * HZ; // 2s 后 进入时钟软中断
add_timer(&hp_timer);
down(&hp_sem);// 非法操作,若获取不到资源,则会崩溃,此处继续执行
// msleep(2000); 非法操作
mdelay(2000); // 时钟中断自旋,2s unlock,中断继续执行
spin_unlock(&hp_lock); //经在单核CPU上测试,timer在unlock后才执行。
hp_prt("spin_unlock out!\n");
down(&hp_sem);// 系统在此处阻塞, timer_func执行释放信号量
hp_prt("get sem OK!\n");
hp_prt("mutex_lock start! \n");
mutex_lock(&hp_mutex); //只能在进程上下文使用
msleep(2000);
mutex_unlock(&hp_mutex);
hp_prt("timer_func mutex_lock out \n");
return 0;
}
static void code_case_concurrent_exit(void)
{
hp_prt("[%s,%d] enter!\n",__FUNCTION__,__LINE__);
}
MODULE_AUTHOR("hpz");
MODULE_LICENSE("Dual BSD/GPL");
module_init(code_case_concurrent_init);
module_exit(code_case_concurrent_exit);