【Linux】并发解决(上)-中断屏蔽,原子操作

news2024/11/25 6:39:22

在这里插入图片描述

🔥博客主页:PannLZ
🎋系列专栏:《Linux系统之路》
😘欢迎关注:👍点赞🙌收藏✍️留言

文章目录

    • 并发解决
      • 1.中断屏蔽
      • 2.原子操作
        • 2.1整形原子操作
        • 2.2位原子操作
        • 原子变量使用例子


并发解决

1.中断屏蔽

单CPU范围内避免竞态的一种简单而有效的方法是在进入临界区之前屏蔽系统的中断,但是在驱动编程中不值得推荐,驱动通常需要考虑跨平台特点而不假定自己在单核上运行。

CPU一般都具备屏蔽中断和打开中断的功能,这项功能可以保证正在执行的内核执行路径不被中断处理程序所抢占,防止某些竞态条件的发生。具体而言,中断屏蔽将使得中断与进程之间的并发不再发生,而且,由于Linux内核的进程调度等操作都依赖中断来实现,内核抢占进程之间的并发也得以避免了。

中断屏蔽的使用方法为:

local_irq_disable() /* 屏蔽中断*/
. . .
critical section /* 临界区*/
. . .
local_irq_enable() /* 开中断*/

local_irq_disable()和local_irq_enable()都只能禁止和使能本CPU内的中断,因此,并不能解决SMP多CPU引发的竞态。因此,单独使用中断屏蔽通常不是一种值得推荐的避免竞态的方法(换句话说,驱动中使用`local_irq_disable/enable()通常意味着一个bug),它适合与下文将要介绍的自旋锁联合使用。

2.原子操作

原子操作可以保证对一个整型数据的修改是排他性的。

Linux内核提供了一系列函数来实现内核中的原子操作,这些函数又分为两类,分别针对整型变量进行原子操作。位和整型变量的原子操作都依赖于底层CPU的原子操作,与CPU架构密切相关。

对于ARM处理器而言,底层使用LDREXSTREX指令,比如atomic_inc()底层的实现会调用到atomic_add(),其代码如下:

static inline void atomic_add(int i, atomic_t *v)
{
unsigned long tmp;
int result;
prefetchw(&v->counter);
__asm__ __volatile__("@ atomic_add\n"
"1: ldrex %0, [%3]\n"
" add %0, %0, %4\n"
" strex %1, %0, [%3]\n"
" teq %1, #0\n"
" bne 1b"
: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
: "r" (&v->counter), "Ir" (i)
: "cc");
}

LDREX(Load with )用于读取内存中的值,并标记对该段内存的独占访问

STREX(Store with Exclusive Access)在更新内存数值时,会检查该段内存是否已经被标记为独占访问,并以此来决定是否更新内存中的值

STREX Rx, Ry, [Rz]

如果执行这条指令的时候发现已经被标记为独占访问了,则将寄存器Ry中的值更新到寄存器Rz指向的内存,并将寄存器Rx设置成0。指令执行成功后,会将独占访问标记位清除1。而如果执行这条指令的时候发现没有设置独占标记,则不会更新内存,且将寄存器Rx的值设置成1。(也就是将操作结果(成功0或失败1)写入Rx寄存器)

这两个指令的精髓就是,无论有多少个处理器,有多少个地方会申请对同一个内存段进行操作,保证只有最早的更新可以成功,这之后的更新都会失败。失败了就证明对该段内存有访问冲突了1。

BNE是一个条件跳转指令。如果上一条指令的结果不为零(即Z标志位为0),那么程序会跳转到标签1b所在的代码位置。在这里,1b是一个标签,b表示向后跳转,1表示跳转到第一个出现的这样的标签

2.1整形原子操作
//1.设置原子变量的值
void atomic_set(atomic_t *v, int i); /* 设置原子变量的值为i */
atomic_t v = ATOMIC_INIT(0); /* 定义原子变量v并初始化为0 */

//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_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);

//上述操作对原子变量执行自增、自减和减操作后(注意没有加),测试其是否为0,为0返回true,否则返回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);
//上述操作对原子变量进行加/减和自增/自减操作,并返回新的值。
2.2位原子操作
//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.测试位
test_bit(nr, void *addr);
//上述操作返回addr地址的第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(nr,void*addr)操作等同于执行test_bit(nr,void*addr)后再执行xxx_bit(nr,void*addr)。
原子变量使用例子

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

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_available);
				return - EBUSY; /* 已经打开*/
}
...
		return 0; /* 成功*/
}

static int xxx_release(struct inode *inode, struct file*filp)
{
		atomic_inc(&xxx_available); /* 释放设备*/
		return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1449508.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux学习】线程池

目录 23.线程池 23.1 什么是线程池 23.2 为什么需要线程池 23.3 线程池的应用场景 23.4 实现一个简单的线程池 23.4.1 RAII风格信号锁 23.4.2 线程的封装 23.4.3 日志打印 22.4.4 定义队列中存放Task类任务 23.4.5 线程池的实现(懒汉模式) 为什么线程池中需要有互斥锁和条件变…

无监督学习:探索数据的潜在结构与规律

目录 前言1. 概念2. 聚类2.1 定义2.2 应用场景 3. 异常检测3.1 定义3.2 应用场景 结语 前言 在当今信息时代,数据扮演着至关重要的角色,其海量、多样的形式为我们提供了前所未有的机会和挑战。在这个大数据的背景下,无监督学习的概念日益引起…

《剑指offer》--字符串左旋【超详细建议收藏】

字符串左旋的三种方法 1. 一个一个字符挪2. 库函数---strcpy和strncat3. 三段逆置法 1. 一个一个字符挪 代码实现如下&#xff1a; #include <stdio.h> #include <string.h>void Left_Reverse(char* str,int k) {int len strlen(str);//6int time 0;time k % …

开源≠不赚钱,开源软件盈利的7大模式。

开源不是目的&#xff0c;目的是圈用户&#xff0c;留住用户&#xff0c;盈利自然不成问题。 开源系统可以通过多种方式赚钱&#xff0c;以下是其中几种常见的方式&#xff1a; 提供付费支持&#xff1a; 开源系统可以提供付费的技术支持服务&#xff0c;包括安装、配置、维…

代码随想录 Leetcode406. 根据身高重建队列

题目&#xff1a; 代码(首刷看解析 2024年2月15日&#xff09;&#xff1a; class Solution { static bool cmp(const vector<int>& A, const vector<int>& B) {if(A[0] B[0]) return A[1] < B[1];return A[0] > B[0]; } public:vector<vector&…

2001-2022年368个地级市平均气温数据

2001-2022年368个地级市平均气温数据 1、时间:2001-2022年 2、范围&#xff1a;368个地级市 3、来源&#xff1a;基于NOAA下属NCEI提供的原始数据编制而成的。 4、指标&#xff1a;年份、省份、省份代码、城市、城市代码、平均气温 5、指标解释&#xff1a;平均气温指某一…

Java奇缘:林浩然与杨凌芸的数学冒险记

Java奇缘&#xff1a;林浩然与杨凌芸的数学冒险记 Java Adventure: The Mathematical Odyssey of Lin Haoran and Yang Lingyun 在Java编程世界的某一个角落&#xff0c;住着两位才华横溢的程序员——林浩然和杨凌芸。林浩然&#xff0c;人称“算法大侠”&#xff0c;对Java Ma…

Linux第56步_根文件系统第3步_将busybox构建的根文件系统烧录到EMMC

1、第1次将“rootfs”打包 1)、打开第1个终端&#xff0c;准备在“mnt”目录下创建挂载目录“rootfs”&#xff1b; 输入“ls回车” 输入“cd /mnt回车” 输入“ls回车”&#xff0c;查看“mnt”目录下的文件和文件夹 输入“sudo mkdir rootfs回车”&#xff0c;在“mnt”…

对进程与线程的理解

目录 1、进程/任务&#xff08;Process/Task&#xff09; 2、进程控制块抽象(PCB Process Control Block) 2.1、PCB重要属性 2.2、PCB中支持进程调度的一些属性 3、 内存分配 —— 内存管理&#xff08;Memory Manage&#xff09; 4、线程&#xff08;Thread&#xff09;…

程序员搞什么副业才有性价比?

干一行恨一行&#xff0c;三百六十行&#xff0c;行行干破防&#xff01; 一份稳定的主业固然重要&#xff0c;但是有性价比的副业更令人心动。朝九晚五的工作日复一日&#xff0c;当然也可能是996的生活反复捶打。从整体来讲&#xff0c;程序员算是高收入群体&#xff0c;但往…

前端秘法进阶篇之事件循环

目录 一.浏览器的进程模型 1.进程 2.线程 二.浏览器的进程和线程 1. 浏览器进程 2. 网络进程 3. 渲染进程 三.渲染主线程 四.异步 五.优先级 1. 延时队列&#xff1a; 2.交互队列&#xff1a; 3.微队列&#xff1a; 六.JS 的事件循环 附加:JS 中的计时器能做到精…

java数据结构与算法基础-----字符串------KMP算法

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 一、概述二、KMP思想三、代码实现 一、概述 什么是KMP算法 我们…

43.1k star, 免费开源的 markdown 编辑器

简介 项目名&#xff1a; MarkText-- 简单而优雅的开源 Markdown 编辑器 Github 开源地址&#xff1a; https://github.com/marktext/marktext 官网&#xff1a; https://www.marktext.cc/ 支持平台&#xff1a; Linux, macOS 以及 Windows。 操作界面&#xff1a; 在操作界…

猫头虎分享已解决Bug || DNS解析问题(DNS Resolution Issue):DNSLookupFailure, DNSResolveError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

猫头虎分享已解决Bug || Error: Minified React Error #130

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

Shell 学习笔记(一)-Shell脚本编程简介

一 什么是shell&#xff1f; shell是一个用 C 语言编写的程序&#xff0c;它是用户使用 Linux 的桥梁。Shell 既是一种命令语言&#xff0c;又是一种程序设计语言。 Shell 是指一种应用程序&#xff0c;这个应用程序提供了一个界面&#xff0c;用户通过这个界面访问操作系统内…

Go教程-什么是编程?

什么是编程&#xff0c;这是个有趣的话题。 编程是什么 编程&#xff0c;字面意思即编写程序&#xff0c;即通过既定的关键字&#xff0c;来描述你的想法&#xff0c;并让计算机的各个部件按照你的想法来做事。 这里计算机的各个部件通常来说&#xff0c;指的是CPU和IO设备。…

numpy 查漏补缺

1. iterating 2. 3. 4. 5. 6. 7. 8. 9.

Python一些可能用的到的函数系列124 GlobalFunc

说明 GlobalFunc是算网的下一代核心数据处理基础。 算网是一个分布式网络&#xff0c;为了能够实现真的分布式计算&#xff08;加快大规模任务执行效率&#xff09;&#xff0c;以及能够在很长的时间内维护不同版本的计算方法&#xff0c;需要这样一个对象/服务来支撑。Globa…

Apache httpd 换行解析漏洞复现(CVE-2017-15715)

Web页面&#xff1a; 新建一个一句话木马&#xff1a; 0.php <?php system($_GET[0]); ?> 上传木马&#xff0c; burpsuite 抓包。 直接上传是回显 bad file。 我们查看数据包的二进制内容&#xff08;hex&#xff09;&#xff0c;内容是以16进制显示的&#xff0c;…