嵌入式Linux应用开发-驱动大全-第一章同步与互斥④

news2024/11/20 20:34:24

嵌入式Linux应用开发-驱动大全-第一章同步与互斥④

  • 第一章 同步与互斥④
    • 1.5 自旋锁spinlock的实现
      • 1.5.1 自旋锁的内核结构体
      • 1.5.2 spinlock在UP系统中的实现
      • 1.5.3 spinlock在SMP系统中的实现
    • 1.6 信号量semaphore的实现
      • 1.6.1 semaphore的内核结构体
      • 1.6.2 down函数的实现
      • 1.6.3 up函数的实现
    • 1.7 互斥量mutex的实现
      • 1.7.1 mutex的内核结构体
      • 1.7.2 mutex_lock函数的实现
        • 1.7.2.1 fastpath
        • 1.7.2.2 slowpath
      • 1.7.3 mutex_unlock函数的实现
        • 1.7.3.1 fastpath
        • 1.7.3.2 slowpath

第一章 同步与互斥④

在这里插入图片描述

1.5 自旋锁spinlock的实现

自旋锁,顾名思义:自己在原地打转,等待资源可用,一旦可用就上锁霸占它。
问题来了,假设别人已经上锁了,你原地打转会占住 CPU资源了,别的程序怎么运行?它没有 CPU怎么解锁?
这个问题,有 2个答案:
① 原地打转的是 CPU x,以后 CPU y会解锁:这涉及多个 CPU,适用于 SMP系统;
② 对于单 CPU系统,自旋锁的“自旋”功能就去掉了:只剩下禁止抢占、禁止中断
我先禁止别的线程来打断我(preempt_disable),我慢慢享用临界资源,用完再使能系统抢占(preempt_enable),这样别人就可以来抢资源了。
注意:SMP就是 Symmetric Multi-Processors,对称多处理器;UP即 Uni-Processor,系统只有一个单核 CPU。
要理解 spinlock,要通过 2个情景来分析:
① 一开始,怎么争抢资源?不能 2个程序都抢到。 这挺好解决,使用原子变量就可以实现。
② 某个程序已经获得资源,怎么防止别人来同时使用这个资源。
这是使用 spinlock时要注意的地方,对应会有不同的衍生函数(_bh/_irq/_irqsave/_restore)。

1.5.1 自旋锁的内核结构体

spinlock对应的结构体如下定义,不同的架构可能有不同的实现:
在这里插入图片描述

上述__raw_tickets结构体中有 owner、next两个成员,这是在 SMP系统中实现 spinlock的关键。

1.5.2 spinlock在UP系统中的实现

对于“自旋锁”,它的本意是:如果还没获得锁,我就原地打转等待。等待谁释放锁? ① 其他 CPU
② 其他进程/线程
对于单 CPU系统,没有“其他 CPU”;如果内核不支持 preempt,当前在内核态执行的线程也不可能被其他线程抢占,也就“没有其他进程/线程”。所以,对于不支持 preempt的单 CPU系统,spin_lock是空函数,不需要做其他事情。
如果单 CPU系统的内核支持 preempt,即当前线程正在执行内核态函数时,它是有可能被别的线程抢占的。这时 spin_lock的实现就是调用“preempt_disable()”:你想抢我,我干脆禁止你运行。
在 UP系统中,spin_lock函数定义如下:
在这里插入图片描述

从以上代码可知,在 UP系统中 spin_lock()就退化为 preempt_disable(),如果用的内核不支持 preempt,那么 spin_lock()什么事都不用做。
对于 spin_lock_irq(),在 UP系统中就退化为 local_irq_disable()和 preempt_disable(),如下图所示:
在这里插入图片描述
假设程序 A要访问临界资源,可能会有中断也来访问临界资源,可能会有程序 B也来访问临界资源,那么使用 spin_lock_irq()来保护临界资源:先禁止中断防止中断来抢,再禁止 preempt防止其他进程来抢。
对于 spin_lock_bh(),在 UP系统中就退化为禁止软件中断和 preempt_disable(),如下图所示:
在这里插入图片描述

对于 spin_lock_irqsave,它跟 spin_lock_irq类似,只不过它是先保存中断状态再禁止中断,如下:
在这里插入图片描述

对应的 spin_unlock函数就不再讲解。

1.5.3 spinlock在SMP系统中的实现

要让多 CPU中只能有一个获得临界资源,使用原子变量就可以实现。但是还要保证公平,先到先得。比如有 CPU0、CPU1、CPU2都调用 spin_lock想获得临界资源,谁先申请谁先获得。
要想理解 SMP系统中 spinlock的实现,得举一个例子。感谢这篇文章:
Linux内核同步机制之(四):spin lock
http://www.wowotech.net/kernel_synchronization/spinlock.html
wowotech真是一个神奇的网站,里面 Linux文章的作者统一标为“linuxer”,牛!
我借用这篇文章的例子讲解,餐厅里只有一个座位,去吃饭的人都得先取号、等叫号。注意,有 2个动作:顾客从取号机取号,电子叫号牌叫号。
① 一开始取号机待取号码为 0
② 顾客 A从取号机得到号码 0,电子叫号牌显示 0,顾客 A上座;
取号机显示下一个待取号码为 1。
③ 顾客 B从取号机得到号码 1,电子叫号牌还显示为 0,顾客 B等待;
取号机显示下一个待取号码为 2。
④ 顾客 C从取号机得到号码 2,电子叫号牌还显示为 0,顾客 C等待;
取号机显示下一个待取号码为 3。
⑤ 顾客 A吃完离座,电子叫号牌显示为 1,顾客 B的号码等于 1,他上座;
⑥ 顾客 B吃完离座,电子叫号牌显示为 2,顾客 C的号码等于 2,他上座;
在这个例子中有 2个号码:取号机显示的“下一个号码”,顾客取号后它会自动加 1;电子叫号牌显示
“当前号码”,顾客离座后它会自动加 1。某个客户手上拿到的号码等于电子叫号牌的号码时,该客户上座。 在这个过程中,即使顾客 B、C同时到店,只要保证他们从取号机上得到的号码不同,他们就不会打架。
所以,关键点在于:取号机的号码发放,必须互斥,保证客户的号码互不相同。而电子叫号牌上号码的变动不需要保护,只有顾客离开后它才会变化,没人争抢它。
在 ARMv6及以上的 ARM架构中,支持 SMP系统。它的 spinlock结构体定义如下:
在这里插入图片描述

owner就相当于电子叫号牌,现在谁在吃饭。next就当于于取号机,下一个号码是什么。每一个 CPU从取号机上取到的号码保存在 spin_lock函数中的局部变量里。
spin_lock函数调用关系如下,核心是 arch_spin_lock:
在这里插入图片描述

arch_spin_lock代码如下:
在这里插入图片描述

图中的注释把原理讲得非常清楚了,即使不同的个体去同时取号,也可以保证取到的号码各不相同。

假设第 1个程序取到了号码,它访问了临界资源后,调用 spin_unlock,代码如下:
在这里插入图片描述

假如有其他程序正在 spin_lock函数中循环等待,它就会立刻判断自己手上的 next是否等于lock->tickets.owner,如果相等就表示输到它获得了锁。
深入分析_linux_spinlock_实现机制
https://blog.csdn.net/electrombile/article/details/51289813
深入分析 Linux自旋锁
http://blog.chinaunix.net/uid-20543672-id-3252604.html
Linux内核同步机制之(四):spin lock
http://www.wowotech.net/kernel_synchronization/spinlock.html

1.6 信号量semaphore的实现

1.6.1 semaphore的内核结构体

注意:这是信号量,不是信号。在前面学习异步通知时,驱动程序给应用程序发信号。现在我们讲的信号量是一种同步、互斥机制。
信号量的定义及操作函数都在 Linux内核文件 include\linux\semaphore.h中定义,如下:
在这里插入图片描述

初始化 semaphore之后,就可以使用 down函数或其他衍生版本来获取信号量,使用 up函数释放信号量。我们只分析 down、up函数的实现。

1.6.2 down函数的实现

如果 semaphore中的 count大于 0,那么 down函数就可以获得信号量;否则就休眠。在读取、修改 count时,要使用 spinlock来实现互斥。
休眠时,要把当前进程放在 semaphore的 wait_list链表中,别的进程释放信号量时去 wait_list中把进程取出、唤醒。
代码如下:
在这里插入图片描述

1.6.3 up函数的实现

如果有其他进程在等待信号量,则 count值无需调整,直接取出第 1个等待信号量的进程,把信号量给它,共把它唤醒。
如果没有其他进程在等待信号量,则调整 count。
整个过程需要使用 spinlock来保护,代码如下:
在这里插入图片描述

1.7 互斥量mutex的实现

1.7.1 mutex的内核结构体

mutex的定义及操作函数都在 Linux内核文件 include\linux\mutex.h中定义,如下:
在这里插入图片描述

初始化 mutex之后,就可以使用 mutex_lock函数或其他衍生版本来获取信号量,使用 mutex_unlock函数释放信号量。我们只分析 mutex_lock、mutex_unlock函数的实现。
这里要堪误一下:前面的视频里我们说 mutex中的 owner是用来记录获得 mutex的进程,以后必须由它来释放 mutex。这是错的!
从上面的代码可知,owner并不一定存在!
owner有 2个用途:debug(CONFIG_DEBUG_MUTEXES)或 spin_on_owner(CONFIG_MUTEX_SPIN_ON_OWNER)。 什么叫 spin on owner?
我们使用mutex的目的一般是用来保护一小段代码,这段代码运行的时间很快。这意味着一个获得mutex的进程,可能很快就会释放掉 mutex。
针对这点可以进行优化,特别是当前获得 mutex的进程是在别的 CPU上运行、并且“我”是唯一等待这个 mutex的进程。在这种情况下,那“我”就原地 spin等待吧:懒得去休眠了,休眠又唤醒就太慢了。
所以,mutex是做了特殊的优化,比 semaphore效率更高。但是在代码上,并没有要求“谁获得 mutex,就必须由谁释放 mutex”,只是在使用惯例上是“谁获得 mutex,就必须由谁释放 mutex”。

1.7.2 mutex_lock函数的实现

1.7.2.1 fastpath

mutex的设计非常精巧,比 semaphore复杂,但是更高效。
首先要知道 mutex的操作函数中有 fastpath、slowpath两条路径(快速、慢速):如果 fastpath成功,就不必使用 slowpath。
怎么理解?
这需要把 metex中的 count值再扩展一下,之前说它只有 1、0两个取值,1表示 unlocked,0表示locked,还有一类值“负数”表示“locked,并且可能有其他程序在等待”。
代码如下:
在这里插入图片描述

先看看 fastpath的函数:__mutex_fastpath_lock,这个函数在下面 2个文件中都有定义:

include/asm-generic/mutex-xchg.h 
include/asm-generic/mutex-dec.h 

使用哪一个文件呢?看看 arch/arm/include/asm/mutex.h,内容如下:

#if __LINUX_ARM_ARCH__ < 6 
#include <asm-generic/mutex-xchg.h> 
#else 
#include <asm-generic/mutex-dec.h> 
#endif 

所以,对于 ARMv6以下的架构,使用 include/asm-generic/mutex-xchg.h中的__mutex_fastpath_lock函数;对于 ARMv6及以上的架构,使用 include/asm-generic/mutex-dec.h中的__mutex_fastpath_lock函数。这 2个文件中的__mutex_fastpath_lock函数是类似的,mutex-dec.h中的代码如下:
在这里插入图片描述

大部分情况下,mutex当前值都是 1,所以通过 fastpath函数可以非常快速地获得 mutex。

1.7.2.2 slowpath

如果 mutex当前值是 0或负数,则需要调用__mutex_lock_slowpath慢慢处理:可能会休眠等待。
在这里插入图片描述

__mutex_lock_common函数也是在内核文件 kernel/locking/mutex.c中实现的,下面分段讲解。
① 分析第一段代码:
在这里插入图片描述

② 分析第二段代码:
在这里插入图片描述

③ 分析第三段代码:
在这里插入图片描述

这个 wait_list是 FIFO(Firt In Firs Out),谁先排队,谁就可以先得到 mutex。

④ 分析第四段代码:for循环,这是重点
在这里插入图片描述

⑤ 分析第五段代码:收尾工作
在这里插入图片描述

1.7.3 mutex_unlock函数的实现

mutex_unlock函数中也有 fastpath、slowpath两条路径(快速、慢速):如果 fastpath成功,就不必使用 slowpath。
代码如下:
在这里插入图片描述

1.7.3.1 fastpath

先看看 fastpath的函数:__mutex_fastpath_lock,这个函数在下面 2个文件中都有定义:

include/asm-generic/mutex-xchg.h 
include/asm-generic/mutex-dec.h 

使用哪一个文件呢?看看 arch/arm/include/asm/mutex.h,内容如下:

#if __LINUX_ARM_ARCH__ < 6 
#include <asm-generic/mutex-xchg.h> 
#else 
#include <asm-generic/mutex-dec.h> 
#endif 

所以,对于 ARMv6以下的架构,使用 include/asm-generic/mutex-xchg.h中的__mutex_fastpath_lock函数;对于 ARMv6及以上的架构,使用 include/asm-generic/mutex-dec.h中的__mutex_fastpath_lock函数。这 2个文件中的__mutex_fastpath_lock函数是类似的,mutex-dec.h中的代码如下:

大部分情况下,加 1后 mutex的值都是 1,表示无人等待 mutex,所以通过 fastpath函数直接增加 mutex的 count值为 1就可以了。
如果 mutex的值加 1后还在这里插入图片描述
是小于等于 0,就表示有人在等待 mutex,需要去 wait_list把它取出唤醒,这需要用到 slowpath的函数:__mutex_unlock_slowpath。

1.7.3.2 slowpath

如果 mutex当前值是 0或负数,则需要调用__mutex_unlock_slowpath慢慢处理:需要唤醒其他进程。
在这里插入图片描述

__mutex_unlock_common_slowpath函数代码如下,主要工作就是从 wait_list中取出并唤醒第 1个进程:
在这里插入图片描述

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

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

相关文章

Android etc1tool之png图片转换pkm 和 zipalign简介

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、etc1tool2.1、用法 三、zipalign3.1 使用 四…

day49 ARM

.text .globl _start _start:mov r1,#1mov r2,#0mov r3,#100 fun2:cmp r2,r3bcc fun1 stop:b stop fun1: ADD r2,r2,r1add r4,r4,r2b fun2 .end

华为云云耀云服务器L实例评测|部署个人在线电子书库 calibre

华为云云耀云服务器L实例评测&#xff5c;部署个人在线电子书库 calibre 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 应用场景1.3 支持镜像 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 calibre3.1 calibre 介绍3.2 Docker 环境搭建3.3 c…

javaScript-事件循环-微任务-宏任务

为什么引入事件循环?如何理解&#xff1f; js是单线程的语言&#xff0c;需要把异步任务交给宿主浏览器执行&#xff0c;仿制js引擎堵塞 以下面的代码为例 异步的代码交给浏览器之后 进入队列中等待被调用&#xff1a; <!DOCTYPE html> <html lang"en"…

嵌入式Linux应用开发-驱动大全-第一章同步与互斥③

嵌入式Linux应用开发-驱动大全-第一章同步与互斥③ 第一章 同步与互斥③1.4 Linux锁的介绍与使用1.4.1 锁的类型1.4.1.1 自旋锁1.4.1.2 睡眠锁 1.4.2 锁的内核函数1.4.2.1 自旋锁1.4.2.2 信号量1.4.2.3 互斥量1.4.2.4 semaphore和 mutex的区别 1.4.3 何时用何种锁1.4.4 内核抢占…

CharacterEncodingFilter的用法

CharacterEncoding是SpringMVC提供的一个一个过滤器,用于设置请求和响应的字符编码,解决乱码问题,他本身是一个过滤器 那么在SpringBoot中,CharacterEncoding就有一个很好的秒用 setEncoding("UTF-8")设置编码 setForceEncoding(true) 设置请求和响应编码 还需要在配…

树的存储结构以及树,二叉树,森林之间的转换

目录 1.双亲表示法 2.孩子链表 3.孩子兄弟表示法 4.树与二叉树的转换 &#xff08;1&#xff09;树转换为二叉树 &#xff08;2&#xff09;二叉树转换成树 5.二叉树与森林的转化 &#xff08;1&#xff09;森林转换为二叉树 以下树为例 1.双亲表示法 双亲表示法定义了…

javaee之通用mapper

通用mapper可以帮我们写sql语句 我们需要引入依赖是 通用mapper的核心依赖 它本身就依赖一个jpa的依赖&#xff0c;通用mapper的整体依赖就包含了通用mapper的核心依赖 下面说一下通用mapper里面的常见注解 KeySql的用法 tk.mybatis.mapper.common.Mapper 这个是通用Mapper的一…

c#设计模式-行为型模式 之 模板方法模式

&#x1f680;简介 模板方法模式定义了一个操作中的算法的骨架&#xff0c;而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下&#xff0c;重新定义算法中的某些步骤。通常用于应对在开发中设计一个系统时知道了算法所需的关键步骤&#xff0c;而且确定…

用 Pycharm 远程连接 Linux 服务器——超详细

用 Pycharm 远程连接 Linux 服务器——超详细 一、介绍二、要求三、服务器配置四、Pycharm远程连接Linux服务器 实战 一、介绍 本人是做NLP的&#xff0c;pycharm写的项目&#xff0c;数据集很大&#xff0c;在自己电脑上运行很慢&#xff0c;但是放到服务器上跑就很快。下面详…

FileZila 实现wind10与Linux系统文件互传

【FileZila】实现windows与Linux系统文件互传

哨兵(Sentinel-1、2)数据下载

哨兵&#xff08;Sentinel-1、2&#xff09;数据下载 一、登陆欧空局网站 二、检索 先下载2号为光学数据 分为S2A和S2B&#xff0c;产品种类有1C和2A&#xff0c;区别就是2A是做好大气校正的影像&#xff0c;当然数量也会少一些&#xff0c;云量检索条件中记得要按格式&#x…

Covert Communication隐蔽通信论文复现

文章目录 前言Covert Communications: A Comprehensive Surveyabstract简介隐蔽通信的概念和机制隐蔽通信的简要历史经典的Alice-Bob-Willie Model与其他安全技术的区别 一、Limits of Reliable Communication with Low Probability of Detection on AWGN Channels摘要introduc…

STM32复习笔记(三):串口

目录 Preface&#xff1a; &#xff08;一&#xff09;CUBEMX配置串口 &#xff08;二&#xff09;轮询方式 &#xff08;三&#xff09;中断 DMA Preface&#xff1a; 串口通信协议简单&#xff0c;因此被广泛应用&#xff1b;串口有UART&#xff08;Universal Asynchron…

谷歌注册手机号码无法验证

1. 打开设置,在语言中点击添加语言搜索English并添加 2. 点击添加后把首选语言换成英语 3. 然后重启浏览器&#xff0c;这时候浏览器就是英文了&#xff0c;最后打开注册页面就能接收短信了

微服务技术栈-认识微服务和第一个微服务Demo

文章目录 前言一、认识微服务二、微服务技术栈三、Eureka注册中心四、微服务DEMO1、搭建eureka-server2、服务注册和服务发现 总结 前言 随着业务的不断复杂&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。 本章就从微服…

vuejs中缓存组件状态-keepAlive

前言 在 vuejs中&#xff0c;我们经常需要缓存一些组件的状态&#xff0c;比如用户登录后&#xff0c;切换到其他页面&#xff0c;再切换回来&#xff0c;需要保留之前的登录状态&#xff0c;而不是重新登录。 或者在切换不同组件的时候&#xff0c;需要保留之前的组件状态&…

[Linux]线程同步

[Linux]线程同步 文章目录 [Linux]线程同步线程同步线程饥饿问题概念 线程同步控制--条件变量pthread_cond_init函数pthread_cond_destroy函数pthread_cond_wait函数pthread_cond_signal函数pthread_cond_broadcast函数条件变量相关函数的使用示例生产者消费者模型基于Blocking…

基于SpringBoot的体育馆场地赛事预约管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

【生命周期】

生命周期 1 引出生命周期2 分析生命周期3 总结生命周期 1 引出生命周期 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta …