当前位置: 首页 > web >正文

imx6ull-驱动开发篇14——原子操作

目录

并发与竞争

原子操作

概念

特性

API 函数

定义原子变量

整形操作 API 函数

位操作 API 函数


并发与竞争

并发(Concurrency)指多个执行单元(线程/进程/中断)同时访问共享资源的现象,

竞争(Race Condition)是并发导致的数据不一致问题。

Linux 系统并发产生的主要原因有以下几个:

  • 多线程并发访问, Linux 是多任务(线程)的系统,所以多线程访问是最基本的原因。
  • 抢占式并发访问,从 2.6 版本内核开始, Linux 内核支持抢占,也就是说调度程序可以在任意时刻抢占正在运行的线程,从而运行其他的线程。
  • 中断程序并发访问。
  •  SMP(多核)核间并发访问,现在 ARM 架构的多核 SOC 很常见,多核 CPU 存在核间并发访问。

如果多个线程同时操作临界区,就表示存在竞争,我们在编写驱动的时候一定要注意避免并发和防止竞争访问。

Linux 内核提供的几种并发和竞争的处理方法如下:

本讲内容我们主要学习一下原子操作。

原子操作

概念

原子操作就是指不能再进一步分割的操作,一般原子操作用于变量或者位操作。

为什么需要原子操作呢?举个例子:

a = 3

这个C语言代码编译为成汇编指令,可能如下:

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

假设现在线程 A要向 a 变量写入 10 这个值,而线程 B 也要向 a 变量写入 20 这个值。

实际上的执行流程可能如图:

结果就是,线程 A 最终将变量 a 设置为了 20。这就是设置变量值时可能遇到的并发与竞争的例子。

解决方法就是让三行汇编指令作为一个整体运行,也就是作为一个原子存在。

特性

原子操作的特性如下:

特性​

​说明​

​不可分割性​

操作要么完整执行,要么完全不执行,不会被线程/中断打断

​无锁机制​

无需使用锁(如自旋锁、互斥锁),直接通过CPU指令实现原子性

​指令级原子性​

由CPU提供的原子指令(如x86的LOCK前缀指令、ARM的LDREX/STREX)保证

​内存屏障​

隐含编译器和CPU内存屏障,确保操作顺序符合预期

​SMP安全​

多核系统中,操作对全部CPU核心可见且有序

​中断安全​

可在中断上下文安全使用(如中断处理函数)

原子操作的优点如下:

优势​

​详细说明​

​零锁争用​

避免锁带来的性能损耗(如上下文切换、缓存同步)

​极低延迟​

通常1-3个CPU周期完成操作,远快于锁机制(微秒级)

​无死锁风险​

不涉及锁的获取/释放,彻底避免死锁问题

​适用场景广​

可安全用于中断、进程、多核并发等任何上下文

​代码简洁​

单条语句完成操作,无需复杂的锁管理逻辑

​硬件加速​

现代CPU直接支持原子指令(如CAS、Fetch-And-Add),效率接近普通变量操作

API 函数

Linux 内核提供了一系列原子操作 API 函数:

  • 整形变量进行操作的API 函数。
  • 进行操作的API 函数

定义原子变量

Linux 内核定义了叫做 atomic_t 的结构体来完成整形数据的原子操作,在使用中用原子变量来代替整形变量。

此结构体定义在 include/linux/types.h 文件中,定义如下:

typedef struct {int counter;
} atomic_t;

使用示例:

atomic_t a; //定义 aatomic_t b = ATOMIC_INIT(0); //定义原子变量 b 并赋初值为 0

如果使用 64 位的 SOC 的话,就要用到 64 位的原子变量:

typedef struct {long long counter;
} atomic64_t;

整形操作 API 函数

Linux 内核提供了对原子变量进行操作,比如读、写、增加、减少等等函数,如表:

示例代码:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/atomic.h>static atomic_t counter = ATOMIC_INIT(100);  // 初始化原子变量为100static int __init atomic_demo_init(void)
{printk("=== Atomic Operations Demo ===\n");// 1. 基础读写操作printk("Initial value: %d\n", atomic_read(&counter));atomic_set(&counter, 50);printk("After set(50): %d\n", atomic_read(&counter));// 2. 算术运算atomic_add(10, &counter);      // 50 + 10 = 60atomic_sub(5, &counter);       // 60 - 5 = 55atomic_inc(&counter);          // 55 + 1 = 56atomic_dec(&counter);          // 56 - 1 = 55printk("After math ops: %d\n", atomic_read(&counter));// 3. 带返回值的操作printk("inc_return: %d\n", atomic_inc_return(&counter));  // 55→56printk("dec_return: %d\n", atomic_dec_return(&counter));  // 56→55// 4. 条件测试操作printk("sub_and_test(55): %d\n", atomic_sub_and_test(55, &counter));  // 55-55=0 → true(1)atomic_set(&counter, 10);printk("dec_and_test: %d\n", atomic_dec_and_test(&counter));  // 10→9 → false(0)printk("inc_and_test(0): %d\n", atomic_inc_and_test(&counter)); // 9→10 → false(0)printk("add_negative(-20): %d\n", atomic_add_negative(-20, &counter)); // 10+(-20)=-10 → true(1)return 0;
}static void __exit atomic_demo_exit(void)
{printk("Final counter value: %d\n", atomic_read(&counter));
}module_init(atomic_demo_init);
module_exit(atomic_demo_exit);
MODULE_LICENSE("GPL");

位操作 API 函数

linux原子位操作是直接对内存进行操作, API 函数如表:

示例代码:

#include <linux/module.h>
#include <linux/bitops.h>static unsigned long bitflags = 0;  // 32位标志位变量static int __init bitops_demo_init(void)
{printk(KERN_INFO "Bit Operations Demo\n");// 1. 基础位操作set_bit(3, &bitflags);         // 0000 1000clear_bit(3, &bitflags);      // 0000 0000change_bit(5, &bitflags);      // 0010 0000 (0→1)change_bit(5, &bitflags);      // 0000 0000 (1→0)// 2. 测试位状态printk("Bit 2: %d\n", test_bit(2, &bitflags));  // 输出0// 3. 原子测试并修改if (!test_and_set_bit(1, &bitflags)) {  // 0000 0010printk("Bit 1 was 0, now set to 1\n");}if (test_and_clear_bit(1, &bitflags)) {  // 0000 0000printk("Bit 1 was 1, now cleared\n");}if (!test_and_change_bit(0, &bitflags)) {  // 0000 0001printk("Bit 0 changed from 0 to 1\n");}return 0;
}static void __exit bitops_demo_exit(void)
{printk(KERN_INFO "Final bitflags: 0x%lx\n", bitflags);
}module_init(bitops_demo_init);
module_exit(bitops_demo_exit);
MODULE_LICENSE("GPL");
http://www.xdnf.cn/news/17395.html

相关文章:

  • WPF 动画卡顿
  • 机器学习支持向量机(SVM)
  • C++基础学习笔记
  • 谈谈SQL计算存储引擎中的索引和计算
  • 数据结构5-哈希表
  • AI搜索引擎——DeepSeek崛起 || #AIcoding·八月创作之星挑战赛# || 简单版
  • SwiftUI中的键盘快捷键、初始页面控制及网络权限管理解析
  • 安装部署K8S集群环境(实测有效版本)
  • SpringCloud基础
  • sqlite的sql语法与技术架构研究
  • 专题二_滑动窗口_将x减到0的最小操作数
  • 强遮挡场景误检率↓79%!陌讯多模态融合算法在充电桩占位检测的实战优化
  • 等保测评-Nginx中间件
  • 计算机毕业设计java疫情防控形势下的高校食堂订餐管理系统 高校食堂订餐管理系统在疫情防控背景下的设计与实现 疫情防控期间高校食堂线上订餐管理平台
  • 【感知机】感知机(perceptron)学习算法的对偶形式
  • 专题二_滑动窗口_长度最小的子数组
  • OpenAI推出开源GPT-oss-120b与GPT-oss-20b突破性大模型,支持商用与灵活部署!
  • AI代码审查大文档处理技术实践
  • Express框架
  • 机器学习之随机森林(Random Forest)实战案例
  • 一种基于CEEMDAN-小波阈值联合降噪-快速谱峭度(FSK)/基尼谱Ginigram/Autogram的故障诊断 Matlab
  • 动手学深度学习(pytorch版):第一章节——引言
  • Linux---第三天---权限
  • Ethereum: 像Uniswap V3贡献者一样开发,克隆、编译与测试v3-core
  • 二叉树算法之【中序遍历】
  • 最新教程 | CentOS 7 内网环境 Nginx + ECharts 页面离线部署手册(RPM 安装方式)
  • Kotlin中String的==相等比较符
  • TCP 如何保证可靠性
  • 深入解析嵌套事务:原理与应用
  • uniapp vue3中使用pinia 和 pinia持久化(没有使用ts)