Linux驱动开发笔记(十二)并发与竞争
文章目录
- 前言
- 一、并发与竞争的引入
- 1.1 并发
- 1.2 竞争
- 1.3 解决方法
- 二、原子操作
- 2.1 概念
- 2.2 使用方法
- 三、自旋锁
- 3.1 概念
- 3.2 使用方法
- 3.3 自旋锁死锁
- 四、信号量
- 4.1 概念
- 4.2 使用方法
- 五、互斥锁
- 5.1 概念
- 5.2 使用方法
前言
Linux的子系统我们已经大致学习完了,笔者最近相到似乎一直没有好好学习一下并发和竞争这一部分内容(在网络编程中曾经简单提到过Linux应用开发笔记(五)网络编程(二)多线程编程)。
一、并发与竞争的引入
1.1 并发
以下图为例,我们的CPU在同时处理多个任务的时候也可能采取类似“分时复用”的手法,即在不同的工作时间块内切换执行的任务,使得在实际效果上好像认为是这些任务是在同时运行的,这种操作方法我们称为并发。

通常情况下,通过并发执行多个任务,可以充分利用多核处理器,提高程序的执行效率,减少资源的闲置时间。
1.2 竞争
在并发的过程中,经常产生不同的程序共享一个资源的情况,这种行为既可以减少资源但也会产生抢占的问题,这种情况我们称之为竞争。
1.3 解决方法
其实竞争的产生可以理解为是多个线程或进程需要访问和修改相同的资源(如全局变量、文件、数据库等),且没有适当的同步机制。那么如何解决这个问题呢?在Linux中提供了原子操作、自旋锁、互斥锁、信号量等同步机制。

二、原子操作
2.1 概念
原子操作是一种不可分割的操作,确保在多线程或多进程环境下,该操作可以在没有中断的情况下完成。原子操作在执行过程中不会被其他线程或进程打断,确保了数据的一致性和正确性。
在并发编程中,多个线程或进程可能会同时访问和修改共享数据。普通的读写操作可能会引发竞争条件(Race Condition),导致数据不一致。原子操作提供了一种机制,确保共享数据的修改是安全的,即使在高度并发的环境中。
在Linux内核中使用 atomic_t和atomic64_t结构体分别来完成32位系统和64位系统的整形数据原子操作,两个结构体定义在“内核源码/include/linux/types.h”文件中,具体定义如下:
typedef struct {int counter;} atomic_t;#ifdef CONFIG_64BITtypedef struct {long counter;
} atomic64_t;#endif

注:这是64位系统的函数集,如果是32位只需要将函数名中的64删去即可。
2.2 使用方法
我们可以使用以下代码定义一个64位系统的原子整型变量,其实细心的读者可能会发现我们在之前中断实验的时候已经使用过这种方式了,当时为了防止持续进入中断函数导致计数出错。
static atomic64_t v = ATOMIC_INIT(1);//初始化原子类型变量v,并设置为1
之后我们便可以根据对原子量进行赋值来定义不同的状态,从而告诉cpu这个资源已经占用,例如:
//本次的所有实验均为拒绝重复打开驱动
static int open(struct inode *inode,struct file *file)
{//判断是否是重复进入if(atomic64_read(&v) != 1){return -EBUSY;printk("\t This process has opened! \n");}//第一次进入,将v的值设置为0atomic64_set(&v,0);return 0;
}static int release_test(struct inode *inode,struct file *file)
{atomic64_set(&v,1);//将原子类型变量v的值赋1return 0;
}
三、自旋锁
3.1 概念
自旋锁(spin lock)是一种非阻塞锁,也就是说,如果某线程需要获取锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取锁。如果在自旋完成后前面锁定同步资源的线程已经释放了锁,那么该线程便不必阻塞,并且直接获取同步资源,从而避免切换线程的开销。
//表示自旋锁
typedef struct spinlock {union {struct raw_spinlock rlock;#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))struct {u8 __padding[LOCK_PADSIZE];struct lockdep_map dep_map;};
#endif};
} spinlock_t;

上图是自旋锁的相关API,一般来说我们使用这些函数就足够了,下图是中断的自旋锁API,这里仅进行补充。

注:为了保险起见,我们通常选择使用spin_lock_irqsave进行自旋锁获取。
3.2 使用方法
代码如下(示例):
//定义spinlock_t类型的自旋锁变量spinlock_test
static spinlock_t spinlock_test;//定义全局变量flag,flag等于1表示设备没有被打开,等于0则证明设备已经被打开了
static int flag = 1;static int open(struct inode *inode,struct file *file)
{//自旋锁加锁spin_lock(&spinlock_test);if(flag != 1){spin_unlock(&spinlock_test);//自旋锁解锁return -EBUSY;}flag = 0;//自旋锁解锁spin_unlock(&spinlock_test);return 0;
}static int release_test(struct inode *inode,struct file *file)
{spin_lock(&spinlock_test);//自旋锁加锁flag = 1;spin_unlock(&spinlock_test);//自旋锁解锁return 0;
}static int __init init(void)
{//初始化自旋锁spin_lock_init(&spinlock_test);...
}
3.3 自旋锁死锁
自旋锁死锁是指两个或多个事物在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。当多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进,这种情况就是死锁。自旋锁死锁发生存在两种情况:
(1)拥有自旋锁的进程A在内核态阻塞了,内核调度B进程,碰巧B进程也要获得自旋锁,此时B只能自旋转。而此时抢占已经关闭(在单核条件下)不会调度A进程了,B永远自旋,产生死锁。
相应的解决办法是,在自旋锁的使用过程中要尽可能短的时间内拥有自旋锁,而且不能在临界区中调用导致线程休眠的函数。
(2)进程A拥有自旋锁,中断到来,CPU执行中断函数,中断处理函数,中断处理函数需要获得自旋锁,访问共享资源,此时无法获得锁,只能自旋,从而产生死锁。
对于中断引发的死锁,最好的解决方法就是在获取锁之前关闭本地中断,由于Linux内核运行是非常复杂的,很难确定某个时刻的中断状态,因此建议使用 spin_lock_irqsave/spin_unlock_irqrestore,因为这一组函数会保存中断状态,在释放锁的时候会恢复中断状态。
四、信号量
4.1 概念
信号量是操作系统中最典型的用于同步和互斥的手段,本质上是一个全局变量,信号量的值表示控制访问资源的线程数,可以根据实际情况来自行设置,如果在初始化的时候将信号量量值设置为大于1,那么这个信号量就是计数型信号量,允许多个线程同时访问共享资源;如果将信号量量值设置为1,那么这个信号量就是二值信号量,同一时间内只允许一个线程访问共享资源;信号量的值不能小于0,当信号量的值为0时,想访问共享资源的线程必须等待,直到信号量大于0时,等待的线程才可以访问。
//表示一个信号量struct semaphore {raw_spinlock_t lock;unsigned int count;struct list_head wait_list;};

当访问共享资源时,信号量执行“减1”操作,访问完成后再执行“加1”操作,这里的down函数可以理解为减1操作,up函数可以理解为加1操作。
4.2 使用方法
代码如下(示例):
//定义一个semaphore类型的结构体变量semaphore_test
struct semaphore semaphore_test;static int open(struct inode *inode,struct file *file)
{//信号量数量减1down(&semaphore_test);return 0;
}static int release_test(struct inode *inode,struct file *file)
{//信号量数量加1up(&semaphore_test);return 0;
}static int __init init(void)
{//初始化信号量结构体semaphore_test,并设置信号量的数量为1sema_init(&semaphore_test,1);...
}
五、互斥锁
5.1 概念
互斥锁为资源引入一个状态:锁定或者非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁和信号量功能相同,但具体的实现方式是不同的,此外使用互斥锁效率更高、更简洁,所以如果使用到的信号量“量值”为 1,一般将其修改为使用互斥锁实现。
struct mutex {atomic_long_t owner;spinlock_t wait_lock;#ifdef CONFIG_MUTEX_SPIN_ON_OWNERstruct optimistic_spin_queue osq; /* Spinner MCS lock */#endifstruct list_head wait_list;#ifdef CONFIG_DEBUG_MUTEXESvoid *magic;#endif#ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lockdep_map dep_map;#endif};

5.2 使用方法
代码如下(示例):
//定义mutex类型的互斥锁结构体变量mutex_test
struct mutex mutex_test;static int open(struct inode *inode,struct file *file)
{//互斥锁加锁mutex_lock(&mutex_test);return 0;
}static int release_test(struct inode *inode,struct file *file)
{//互斥锁解锁mutex_unlock(&mutex_test);return 0;
}static int __init init(void)
{//对互斥体进行初始化mutex_init(&mutex_test);...
}
免责声明:本内容部分参考野火科技及其他相关公开资料,若有侵权或者勘误请联系作者。
相关文章:
Linux驱动开发笔记(十二)并发与竞争
文章目录 前言一、并发与竞争的引入1.1 并发1.2 竞争1.3 解决方法 二、原子操作2.1 概念2.2 使用方法 三、自旋锁3.1 概念3.2 使用方法3.3 自旋锁死锁 四、信号量4.1 概念4.2 使用方法 五、互斥锁5.1 概念5.2 使用方法 前言 Linux的子系统我们已经大致学习完了,笔者…...
【Mac】Listen 1 for Mac(最强的音乐搜索工具)软件介绍
软件介绍 Listen 1 for Mac 是一款非常方便的音乐播放软件,主要功能是集成多个音乐平台,让用户可以方便地搜索、播放和管理音乐。它是一个用 Python 语言开发的免费开源综合音乐搜索工具项目,最大的亮点在于可以搜索和播放来自网易云音乐&am…...
nginx 1024 worker_connections are not enough while connecting to upstream
现象 请求api响应慢,甚至出现504 gateway timeout,重启后端服务不能恢复,但重启nginx可以恢复。 解决方案 worker_connections使用了默认值 1024,当流量增长时,导致连接不够 在nginx.conf中修改连接数就可以了&…...
在Ubuntu 16.04上安装和配置Elasticsearch的方法
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 Elasticsearch 是一个用于实时分布式搜索和数据分析的平台。它因易用性、强大功能和可扩展性而备受欢迎。 Elasticsearch 支持 R…...
C#给SqlSugar封装一个单例类
.NET兼职社区 可以直接用,轻量方便,无需重复造轮子。 这里只对CRUD进行封装,我的应用比较简单。 using SqlSugar; using System.Collections.Generic;namespace MusicApp.Assist {internal class SqlSugarAssist{private static readonly ob…...
Postman接口测试工具的原理及应用详解(六)
本系列文章简介: 在当今软件开发的世界中,接口测试作为保证软件质量的重要一环,其重要性不言而喻。随着前后端分离开发模式的普及,接口测试已成为连接前后端开发的桥梁,确保前后端之间的数据交互准确无误。在这样的背景…...
【算法 之插入排序 原理及案例】
插入排序原理: 插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常…...
第一节:如何开发第一个spring boot3.x项目(自学Spring boot 3.x的第一天)
大家好,我是网创有方,从今天开始,我会记录每篇我自学spring boot3.x的经验。只要我不偷懒,学完应该很快,哈哈,更新速度尽可能快,想和大佬们一块讨论,如果需要讨论的欢迎一起评论区留…...
JS逆向:由 words 、sigBytes 引发的一系列思考与实践
【作者主页】:小鱼神1024 【擅长领域】:JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等 在做JS逆向时,你是否经常看到 words 和 sigBytes 这两个属性呢,比如ÿ…...
计算机的错误计算(十五)
摘要 介绍历史上由于计算精度问题引起的灾难或事件。 今天换个话题,说说历史上曾经发生过的一些事件。 1961 年 , 美国麻省理工学院气象学家洛伦兹在仿真天气预报时 , 将 0.506127 舍入到 0.506 , 所得计算结果大相径庭 ! 这种“差之毫厘 , 谬以千里”的现象…...
制作img文件
安装软件包 sudo apt-get install dosfstools dump parted kpartx 创建空白img文件 sudo dd if/dev/zero ofraspberrypi.img bs1M count4000 给img文件分区 sudo parted raspberrypi.img --script -- mklabel msdos sudo parted raspberrypi.img --script -- mkpart primar…...
GB28181视频汇聚平台EasyCVR接入Ehome设备视频播放出现异常是什么原因?
多协议接入视频汇聚平台EasyCVR视频监控系统采用了开放式的架构,系统可兼容多协议接入,包括市场标准协议:国标GB/T 28181协议、GA/T 1400协议、JT808、RTMP、RTSP/Onvif协议;以及主流厂家私有协议及SDK,如:…...
Java利用poi实现word,excel,ppt,pdf等各类型文档密码检测
介绍 最近工作上需要对word,excel,ppt,pdf等各类型文档密码检测,对文件进行分类,有密码的和没密码的做区分。查了一堆资料和GPT都不是很满意,最后东拼西凑搞了个相对全面的检测工具代码类,希望能给需要的人带来帮助。 说明 这段…...
顺序表与链表学习笔记
顺序表及其结构定义 (1)结构定义 顺序存储: 顺序表的元素按顺序存储在一块连续的内存区域中,每个元素占用相同大小的存储空间。通过数组实现,每个元素可以通过下标快速访问。 存储密度高: 因为顺序表使用…...
2.SQL注入-字符型
SQL注入-字符型(get) 输入kobe查询出现id和邮箱 猜测语句,字符在数据库中需要用到单引号或者双引号 select 字段1,字段2 from 表名 where usernamekobe;在数据库中查询对应的kobe,根据上图对应上。 select id,email from member where usernamekobe;编写payload语…...
在Ubuntu 14.04上安装和配置Elasticsearch的方法
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 Elasticsearch 是一个用于实时分布式搜索和数据分析的平台。它因易用性、强大功能和可扩展性而备受欢迎。 Elasticsearch 支持 R…...
C++:inline关键字nullptr
inline关键字 C中inline使用关键点强调 (1)inline是一种“用于实现的关键字”,而不是一种“用于声明的关键字”,所以关键字 inline 必须与函数定义体放在一起,而不是和声明放在一起 (2)如果希望在多个c文件中使用,则inline函数应…...
数字信号处理实验三(IIR数字滤波器设计)
IIR数字滤波器设计(2学时) 要求: 产生一复合信号序列,该序列包含幅度相同的28Hz、50Hz、100Hz、150Hz的单音(单频)信号;其中,50Hz及其谐波为工频干扰(注:采样…...
Why is Kafka fast?(Kafka性能基石)
Kafka概述 Why is kafka fast? 思考一下,当我们在讨论Kafka快的时候我们是在谈论什么呢?What does it even mean that Kafka is fast? 我们是在谈论kafka的低延迟(low latency)还是在讨论吞吐量(through…...
Linux下的SSH详解及Ubuntu教程
前言 SSH(Secure Shell)是一种用于计算机之间安全通信的协议,广泛应用于远程登录、系统管理和文件传输等场景。本文将详细介绍SSH在Linux系统(特别是Ubuntu)下的使用,包括安装、配置、密钥管理和常见应用&…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...
今日行情明日机会——20250609
上证指数放量上涨,接近3400点,个股涨多跌少。 深证放量上涨,但有个小上影线,相对上证走势更弱。 2025年6月9日涨停股主要行业方向分析(基于最新图片数据) 1. 医药(11家涨停) 代表标…...
