Linux中的并发与竞争(1)并发是如何发生的以及原子操作介绍

Linux中的并发与竞争(2)最简单的自旋锁概念及范例

1.并发概念

多任务同时访问同一个共享数据段

例子:A、B、C任务,同时对共享数据段S进行访问
A任务对S写入3000个字符"a",B任务对S写入3000个字符"b",C任务负责读取所有数据,当执行顺序如下时,A将数据从用户态拷贝完成后,还未累加当前访问下标,B便对数据进行了访问,此后C获取数据,获取到的数据并不是3000个字符"a",而是3000个字符"b"。
image.png

2.原子操作

2.1概念

原子操作:不能进一步分割的操作

即使是一句简单的赋值操作,例如a=3,其实也并不是一个原子操作,对应的汇编代码是

ldr r0, =0X30000000 /* 变量 a 地址 */
ldr r1, = 3 /* 要写入的值 */
str r1, [r0] /* 将 3 写入到 a 变量中 */

而当两个任务,同时执行a=3时,可能出现如下的情况,变量最终被设置为20,而不是期望的10
image.png
而如果使用原子操作API,则不会出现上述情况。例如我们的代码中,需要用到一个int类型的标志位,当多线程都需要访问这个标志位时,就会有并发问题。如果使用锁较麻烦,而原子操作可以非常好的实现需求。

Linux定义atomic_t结构体完成整形数据的原子操作,定义在 **include/linux/types.h **文件中

typedef struct {
    int counter;
} atomic_t;

2.2整形操作API

32位整型使用如下API,64位整型则使用atomic64_开头

ATOMIC_INIT(int i) 									//定义原子变量的时候对其初始化。 
int atomic_read(atomic_t *v) 						//读取 v 的值,并且返回。 
void atomic_set(atomic_t *v, int i) 				//向 v 写入 i 值。 
void atomic_add(int i, atomic_t *v) 				//给 v 加上 i 值。 
void atomic_sub(int i, atomic_t *v) 				//从 v 减去 i 值。 
void atomic_inc(atomic_t *v) 						//给 v 加 1,也就是自增。 
void atomic_dec(atomic_t *v) 						//从 v 减 1,也就是自减 
int atomic_dec_return(atomic_t *v) 					//从 v 减 1,并且返回 v 的值。 
int atomic_inc_return(atomic_t *v) 					//给 v 加 1,并且返回 v 的值。 
int atomic_sub_and_test(int i, atomic_t *v) //从 v 减 i,如果结果为 0 就返回真,否则返回假 
int atomic_dec_and_test(atomic_t *v) 		//从 v 减 1,如果结果为 0 就返回真,否则返回假 
int atomic_inc_and_test(atomic_t *v) 		//给 v 加 1,如果结果为 0 就返回真,否则返回假 
int atomic_add_negative(int i, atomic_t *v) //给 v 加 i,如果结果为负就返回真,否则返回假  

示例代码

atomic_t v = ATOMIC_INIT(0); /* 定义并初始化原子变零 v=0 */
atomic_set(&v, 10); /* 设置 v=10 */
atomic_read(&v); /* 读取 v 的值,肯定是 10 */
atomic_inc(&v); /* v 的值加 1,v=11 */

2.3位操作API

void set_bit(int nr, void *p) 						//将 p 地址的第 nr 位置 1。
void clear_bit(int nr,void *p) 						//将 p 地址的第 nr 位清零。
void change_bit(int nr, void *p) 					//将 p 地址的第 nr 位进行翻转。
int test_bit(int nr, void *p) 						//获取 p 地址的第 nr 位的值。
int test_and_set_bit(int nr, void *p) 	 //将 p 地址的第 nr 位置 1,并且返回 nr 位原来的值。
int test_and_clear_bit(int nr, void *p)  //将 p 地址的第 nr 位清零,并且返回 nr 位原来的值。
int test_and_change_bit(int nr, void *p) //将 p 地址的第 nr 位翻转,并且返回 nr 位原来的值。
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux设备驱动,由于多个进程或线程可能会同时访问设备,因此需要进行并发控制以确保设备的正确性和稳定性。以下是一些常用的Linux设备驱动并发控制方法: 1. 互斥锁(mutex):互斥锁是用于保护临界区的一种机制,当一个进程或线程进入临界区时,其他进程或线程必须等待其退出后才能进入。Linux内核提供了多种不同类型的互斥锁,如spinlock、semaphore等,开发者可以根据实际需求选择不同的锁类型。 2. 读写锁(rwlock):读写锁是一种特殊的互斥锁,它允许同时有多个读者访问共享资源,但只允许一个写者访问。读写锁可以提高并发性能,但也需要考虑读写锁的开销。 3. 自旋锁(spinlock):自旋锁是一种忙等待的锁,当一个进程或线程无法获取锁时,它会一直循环尝试获取锁,直到获取成功。自旋锁对于短时间的临界区保护非常有效,但长时间的自旋会浪费CPU资源。 4. 原子操作(atomic):原子操作是一种不可分割的操作,可以保证操作的完整性和一致性。在Linux设备驱动原子操作通常用于对共享变量的操作,如增减计数器等。 除了以上方法,还有一些高级的并发控制技术,如RCU、信号量(semaphore)等,它们可以根据具体的应用场景来选择使用。在开发Linux设备驱动时,需要根据实际情况选择合适的并发控制方法,并注意避免死锁和竞争条件等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值