并发和竞态

1、什么是并发和竞态??

并发:指多个执行单元同时被执行,而并发执行单元对共享资源的访问容易导致竞态(共享资源:硬件资源和软件上的全局变量、静态变量)

2、并行的例子

(1)、多个cup:使用共同的系统总线,因为可以并发访问共同的外设和储存器

(2)、单个cpu:内核支持抢占,高优先级的进程可以打断正在内核执行的低优先级的进程

(3)、中断:(硬中断、软中断、Tasklet、底半部)如果中断处理程序正在访问进程的资源,中断也有可能被新的更高优先级的中断打断,因此,多个中断之间本身也可能引起并发而导致中断

3、怎样解决竞态问题??

互斥访问:一个执行单元在访问共享资源的时候,其他执行单元不能再访问这个资源

临界区:访问共享资源的代码区域称为临界区(critical sections),临界区需要某种互斥机制加以保护

互斥机制:(1)、中断屏蔽 (2)、原子操作 (3)、自旋锁 (4)、信号量

 

下面对几个互质机制分别讲解:

(1)、中断屏蔽

方法:进入临界区之前屏蔽系统中断,保证内核正在执行的进程不被中断处理程序所抢占。(linux内核的调度都依赖中断来实现,这样子内核抢占之间的并发也就避免了)

中断屏蔽使用的方法:

方法1

local_irq_disable()        // 屏蔽中断 (只禁止中断,没有保存中断位信息)    

...

critical section         // 临界区     

...

local_irq_enable()    // 开中断   

 

方法2

local_irq_save(flags)   // 屏蔽中断 (除了禁止终端操作以外,还保存了中端位信息)    

...

critical section

...

local_irq_restore(flags)

 

注意:单独使用中断屏蔽通常不是一种值得推荐的避免竞态的办法,它适合与自旋锁联合使用

(2)、原子操作

原子操作:指的是在执行过程中不会被别的代码路径所中断的操作

说明:linux内核提供了一系列函数来实现内核中的原子操作,这些函数分为两类,分别针对位和整型变量进行的原子操作。他们的共同点是在任何情况下操作都是原子的,内核代码可以安全的调用它们而不被打断。位和整形变量原子操作都依赖于底层cpu的原子操作来实现,因此这些函数都与cpu架构密切相关

 

A、整型原子操作

(1)、设置原子变量的值

atomic_t v = ATOMIC_INIT(0); // 定义原子变量并初始化为0

void atomic_set(atomic_t *v,int i); // 设置原子变量v的值为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_test(atomic_t *v);

int atomic_dec_test(atomic_t *v);

int atomic_sub_test(atomic_t *v);

上述操作对原子变量执行自增、自减和减操作后(注意没有加哦!!),测试有没有变为0。为0则返回真true,不为0则返回false

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

上述操作对原子变量进行加减和自增自减操作,并返回新值

B、位原子操作

(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)、测试位

int test_bit(nr,void *addr);

上述操作返回address地址的第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);

上述test_and_xxx_bit 操作等价于先执行test_bit,在执行set_bit

例子:使用原子变量是设备只能被一个进程打开

static atomic_t xxx_available = ATOMIC_INIT(1);

static int xxx_open(struct inode *inode,struct file *filp);

{

...

if(!atomic_dec_and_test(&xxx_available))

{

atomic_inc(&xxx_available0);

return -EBUSY;

}

...

return 0;

}

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(并发和竞态)