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

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 这两个属性呢,比如&#xff…...

计算机的错误计算(十五)

摘要 介绍历史上由于计算精度问题引起的灾难或事件。 今天换个话题,说说历史上曾经发生过的一些事件。 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)下的使用,包括安装、配置、密钥管理和常见应用&…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...