linux并发与竞争
·
1.学习原因
Linux 是一个多任务操作系统,肯定会存在多个任务共同操作同一段内存或者设备的情况,
多个任务甚至中断都能访问的资源叫做共享资源,就和共享单车一样。在驱动开发中要注意
对共享资源的保护,也就是要处理对共享资源的并发访问。本章我们就来学习一下如何在 Linux 驱动中处理并发。
2.产生并发原因
现在的 Linux 系统并发产生的原因很复杂,总结一下有下面几个主
要原因:
①、多线程并发访问,Linux 是多任务(线程)的系统,所以多线程访问是最基本的原因。
②、抢占式并发访问,从 2.6 版本内核开始,Linux 内核支持抢占,也就是说调度程序可
以在任意时刻抢占正在运行的线程,从而运行其他的线程。
③、中断程序并发访问,这个无需多说,学过 STM32 的同学应该知道,硬件中断的权利
可是很大的。
④、SMP(多核)核间并发访问,现在 ARM 架构的多核 SOC 很常见,多核 CPU 存在核间
并发访问。
并发访问带来的问题就是竞争,学过 FreeRTOS 和 UCOS 的同学应该知道临界区这个概
念,所谓的临界区就是共享数据段,对于临界区必须保证一次只有一个线程访问,也就是要保证临界区是原子访问的。我们都知道,原子化学反应不可再分的基本微粒,这里的原子访问就表示这一个访问是一个步骤,不能再进行拆分。
3.保护对象(数据)
前面一直说要防止并发访问共享资源,换句话说就是要保护共享资源,防止进行并发访问。那么问题来了,什么是共享资源?在程序中什么是共享资源?也就是保护的内容是什么?我们保护的不是代码,而是数据!某个线程的局部变量不需要保护,我们要保护的是多个线程都会访问的共享数据(比如一个整形的全局变量 a )。
4.原子操作
线程A是a=10,线程B是a=20;下面有两种可能:

按照上图所示的流程,线程 A 最终将变量 a 设置为了 20,而并不是要求的 10!线程 B
没有问题。这就是一个最简单的设置变量值的并发与竞争的例子,要解决这个问题就要保证
示例代码 22.2.1 中的三行汇编指令作为一个整体运行,也就是作为一个原子存在。
Linux 内核提供了一组原子操作 API 函数来完成此功能,Linux 内核提供了两组原子操作 API 函数,一组是对整形变量进行操作的,一组是对位进行操作的,我们接下来看一下这些 API 函数。
没有问题。这就是一个最简单的设置变量值的并发与竞争的例子,要解决这个问题就要保证
示例代码 22.2.1 中的三行汇编指令作为一个整体运行,也就是作为一个原子存在。
Linux 内核提供了一组原子操作 API 函数来完成此功能,Linux 内核提供了两组原子操作 API 函数,一组是对整形变量进行操作的,一组是对位进行操作的,我们接下来看一下这些 API 函数。
4.1原子整形操作 API 函数
如果要使用原子操作 API 函数,首先要先定义一个 atomic_t 的变量。
atomic64_t a; //定义 a

示例代码 22.2.4 原子变量和 API 函数使用Atomic64_t v = ATOMIC64_INIT(0);/* 定义并初始化原子变零 v=0 */Atomic64_set(&v, 10);/* 设置 v=10 */Atomic64_read(&v);/* 读取 v 的值,肯定是 10 */Atomic64_inc(&v);/* v 的值加 1,v=11 */
4.2原子位操作 API 函数

5.自旋锁
原子操作只能对整形变量或者位进行保护,但是,在实际的使用环境中怎么可能只有整
形变量或位这么简单的临界区。举个最简单的例子,设备结构体变量就不是整型变量,我们
对于结构体中成员变量的操作也要保证原子性,在线程 A 对结构体变量使用期间,应该禁止
其他的线程来访问此结构体变量,这些工作原子操作都不能胜任,需要本节要讲的锁机制,
在 Linux 内核中就是自旋锁。
当一个线程要访问某个共享资源的时候首先要先获取相应的锁,锁只能被一个线程持有,只要此线程不释放持有的锁,那么其他的线程就不能获取此锁。对于自旋锁而言,如果自旋锁正在被线程 A 持有,线程 B 想要获取自旋锁,那么线程 B 就会处于忙循环-旋转-等待状态, 线程 B 不会进入休眠状态或者说去做其他的处理,而是会一直傻傻的在那里“转圈圈”的等 待锁可用。
在使用自旋锁之前,肯定要先定义一个自旋锁变量,定义方法如下所示:
spinlock_t lock; //定义自旋锁
定义好自旋锁变量以后就可以使用相应的 API 函数来操作自旋锁。



示例代码 22.3.2 自旋锁使用示例1 DEFINE_SPINLOCK(lock);/* 定义并初始化一个自旋锁 */23 /* 线程 A */4 void functionA (){5 unsigned long flags;/* 中断状态 */6 spin_lock_irqsave(&lock, flags);/* 获取锁 */7 /* 临界区 */8 spin_unlock_irqrestore(&lock, flags);/* 释放锁 */9 }1011 /* 中断服务函数 */12 void irq() {13 spin_lock(&lock);/* 获取锁 */14 /* 临界区 */15 spin_unlock(&lock);/* 释放锁 */16 }
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)