AUTOSAR-自旋锁(spinlock)与互斥锁

2023-10-31

AUTOSAR多核OS为实现核间资源互斥,保证数据一致性,设计了自旋锁机制,该机制适用于核间资源互斥。对于多核概念,需要一种新的机制来支持不同内核上任务的互斥。这种新机制不应在同一内核上的 TASK 之间使用,因为它没有意义。在这种情况下,AUTOSAR 操作系统将返回错误。

自旋锁的特点就是当一个线程获取了锁之后,其他试图获取这个锁的线程一直在循环等待获取这个锁,直至锁重新可用。由于线程实在一直循环的获取这个锁,所以会造成CPU处理时间的浪费,因此最好将自旋锁用于能很快处理完的临界区。

一旦一个锁变量被 TASK/ISR2 占用,其他内核上的其他 TASK/ISR2 将无法占用该锁变量。自旋锁机制不会在轮询锁定变量时取消调度这些其他 TASK。但是,在轮询锁定变量时,可能会发生具有更高优先级的 TASK/ISR 准备就绪的情况。在这种情况下,旋转任务将受到干扰。

1. 干扰引起的死锁情况,在同一个核Core0内,高优先级 TASK 无限旋转(循环的获取这个锁),因为低优先级 TASK 已经占用了自旋锁。在这种情况下,第二个 GetSpinlock 调用将返回错误。【当高优先级向低优先级的task一直申请自旋锁的情况】

2. 用户可以保护 TASK 免受【当高优先级向低优先级的task一直申请自旋锁的时,因为低优先级 TASK 已经占用了自旋锁,第二个 GetSpinlock 调用将返回错误】的影响,例如,使用 SuspendAllInterrupts 敲击旋转锁,这样它就不会受到其他 TASKS 的干扰。操作系统可以为调用方自动执行此操作,请参阅配置参数 OsSpinlockLockMethod。

下图显示了由两个不同内核上的 TASKS 以不同顺序获取的两个旋转锁导致的典型死锁

3. 为避免死锁,不允许嵌套不同的旋转锁。如果旋转锁必须要嵌套,则必须定义唯一的顺序。旋转锁只能按此顺序进行,在定义的顺序内不允许循环,允许跳过单个旋转锁。

此图显示了一个示例,其中两个 TASKS 可以访问一组旋转锁 S1 -- S6。允许按预定义的顺序占用旋转锁,并允许跳过旋转锁。如果同时占用多个旋转锁,则必须按严格的后进先出顺序进行锁定和解锁。

4. 小结

AUTOSAR OS为了避免同一个内核上自旋锁造成的死锁,在任何任务或者中断占用自旋锁时,OS会自动挂起所有的中断,不会被同一内核上的任务或者中断抢占。但是,如果核间任务嵌套请求占用自旋锁,就有可能导致任务的相互锁死。比如,核0上的某任务请求先占用自旋锁A,再占用自旋锁B;而核1上的另一个任务请求先占用自旋锁B,再占用自旋锁A。如果这两个任务开始运行的时间间隔很近,就有可能造成任务的互相锁死,即自旋锁的回环嵌套调用导致的死锁。所以在系统设计时,禁止回环嵌套使用自旋锁。如果实在需要嵌套使用自旋锁,那么需要严格按照顺序请求。比如核0上的某任务请求先占用自旋锁A,再占用自旋锁B;核1上的另一个任务也先请求占用自旋锁A,再占用自旋锁B,这样顺序地访问自旋锁就不会造成死锁。

自旋锁使用时有2点需要注意:

  1. 自旋锁是不可递归的,递归的请求同一个自旋锁会自己锁死自己。递归
  2. 线程获取自旋锁之前,要禁止当前处理器上的中断。(防止获取锁的线程和中断形成竞争条件) 
      比如:当前线程获取自旋锁后,在临界区中被中断处理程序打断,中断处理程序正好也要获取这个锁, 
      于是中断处理程序会等待当前线程释放锁,而当前线程也在等待中断执行完后再执行临界区和释放锁的代码。

中断处理下半部的操作中使用自旋锁尤其需要小心:

  1. 下半部处理和进程上下文共享数据时,由于下半部的处理可以抢占进程上下文的代码, 
      所以进程上下文在对共享数据加锁前要禁止下半部的执行,解锁时再允许下半部的执行
  2. 中断处理程序(上半部)和下半部处理共享数据时,由于中断处理(上半部)可以抢占下半部的执行, 
    所以下半部在对共享数据加锁前要禁止中断处理(上半部),解锁时再允许中断的执行
  3. 同一种tasklet不能同时运行,所以同类tasklet中的共享数据不需要保护。
  4. 不同类tasklet中共享数据时,其中一个tasklet获得锁后,不用禁止其他tasklet的执行,因为同一个处理器上不会有tasklet相互抢占的情况
  5. 同类型或者非同类型的软中断在共享数据时,也不用禁止下半部,因为同一个处理器上不会有软中断互相抢占的情况

自旋锁(spinlock)与互斥锁的区别:

加锁的目的就是保证共享资源在任意时间里,只有一个线程访问,这样就可以避免多线程导致共享数据错乱的问题。

当已经有一个线程加锁后,其他线程加锁则就会失败,互斥锁和自旋锁对于加锁失败后的处理方式是不一样的:

  • 互斥锁加锁失败后,线程会释放 CPU ,给其他线程;
  • 自旋锁加锁失败后,线程会忙等待,直到它拿到锁;

互斥锁是一种「独占锁」,比如当线程 A 加锁成功后,此时互斥锁已经被线程 A 独占了,只要线程 A 没有释放手中的锁,线程 B 加锁就会失败,于是就会释放 CPU 让给其他线程,既然线程 B 释放掉了 CPU,自然线程 B 加锁的代码就会被阻塞。

对于互斥锁加锁失败而阻塞的现象,是由操作系统内核实现的。当加锁失败时,内核会将线程置为「睡眠」状态,等到锁被释放后,内核会在合适的时机唤醒线程,当这个线程成功获取到锁后,于是就可以继续执行。所以,互斥锁加锁失败时,会从用户态陷入到内核态,让内核帮我们切换线程,虽然简化了使用锁的难度,但是存在一定的性能开销成本。

那这个开销成本是什么呢?会有两次线程上下文切换的成本:

  • 当线程加锁失败时,内核会把线程的状态从「运行」状态设置为「睡眠」状态,然后把 CPU 切换给其他线程运行;
  • 接着,当锁被释放时,之前「睡眠」状态的线程会变为「就绪」状态,然后内核会在合适的时间,把 CPU 切换给该线程运行。

线程的上下文切换的是什么?

当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据

上下切换的耗时有大佬统计过,大概在几十纳秒到几微秒之间,如果你锁住的代码执行时间比较短,那可能上下文切换的时间都比你锁住的代码执行时间还要长。

所以,如果你能确定被锁住的代码执行时间很短,就不应该用互斥锁,而应该选用自旋锁,否则使用互斥锁

自旋锁是通过 CPU 提供的 CAS 函数(Compare And Swap),在「用户态」完成加锁和解锁操作,不会主动产生线程上下文切换,所以相比互斥锁来说,会快一些,开销也小一些。

一般加锁的过程,包含两个步骤:

  • 第一步,查看锁的状态,如果锁是空闲的,则执行第二步;
  • 第二步,将锁设置为当前线程持有;

CAS 函数就把这两个步骤合并成一条硬件级指令,形成原子指令,这样就保证了这两个步骤是不可分割的,要么一次性执行完两个步骤,要么两个步骤都不执行。

使用自旋锁的时候,当发生多线程竞争锁的情况,加锁失败的线程会「忙等待」,直到它拿到锁。这里的「忙等待」可以用 while 循环等待实现,不过最好是使用 CPU 提供的 PAUSE 指令来实现「忙等待」,因为可以减少循环等待时的耗电量。

自旋锁是最比较简单的一种锁,一直自旋,利用 CPU 周期,直到锁可用。需要注意,在单核 CPU 上,需要抢占式的调度器(即不断通过时钟中断一个线程,运行其他线程)。否则,自旋锁在单 CPU 上无法使用,因为一个自旋的线程永远不会放弃 CPU。

自旋锁开销少,在多核系统下一般不会主动产生线程切换,适合异步、协程等在用户态切换请求的编程方式,但如果被锁住的代码执行时间过长,自旋的线程会长时间占用 CPU 资源,所以自旋的时间和被锁住的代码执行的时间是成「正比」的关系,我们需要清楚的知道这一点。

自旋锁与互斥锁使用层面比较相似,但实现层面上完全不同:当加锁失败时,互斥锁用「线程切换」来应对,自旋锁则用「忙等待」来应对

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

AUTOSAR-自旋锁(spinlock)与互斥锁 的相关文章

随机推荐

  • 时序预测

    时序预测 MATLAB实现Bayes贝叶斯优化LSTM 长短期记忆神经网络 时间序列预测 预测效果一览
  • React - Websocket

    组件didMount调用 Store createWebSocket Math random Store url ws window backend server slice 7 apronMapWebsocket 这个要与后端提供的相同
  • C++函数重载、重写与重定义

    演示代码 include
  • 探索Java8——CompletableFuture: 组合式异步编程

    文章目录 Future接口 Future接口的局限性 使用 CompletableFuture 使用并行流对请求进行并行操作 使用 CompletableFuture 发起异步请求 如果你的意图是实现并发 而非并行 或者你的主要目标是在同一
  • https到底是如何防篡改的

    1 前言 https是一个老生常谈的话题了 也是面试过程种经常甚至必然会问到的一个问题 但当问到https为什么安全的时候 很多人的回答就是简单的回一句 因为他加密了 然后就没然后了 你也相当于啥都没回答出来 2 我为什么要写这篇文章呢 网
  • select底部增加固定按钮

  • 基于SSM的校园快递一站式服务系统设计与实现

    末尾获取源码 开发语言 Java Java开发工具 JDK1 8 后端框架 SSM 前端 采用JSP技术开发 数据库 MySQL5 7和Navicat管理工具结合 服务器 Tomcat8 5 开发软件 IDEA Eclipse 是否Mave
  • 统计学R语言 第五章课后练习 置信区间

    5 1 计算一个总体均值的置信区间 大样本 gt exercise5 1 lt read csv D 289250 统计学 基于R 第4版 例题和习题数据 统计学 基于R 第4版 例题和习题数据 公开资源 exercise chap05 e
  • 用python网络爬虫爬取英雄联盟英雄图片

    用python爬虫爬取lol皮肤 这也用python网络爬虫爬取lol英雄皮肤 忘了是看哪个大神的博客 由于当时学了下就一直放在这儿 现在又才拿出来 再加上马上要考二级挺忙的 代码基本上是没改 还望大神原谅 本人小白 没学过Python 只
  • Pay Cycle related record

    select delete from sysadm ps pycycl stat where pay cycle ACH and pay cycle seq num gt 30 select delete from sysadm ps PY
  • Golang初入编程-踩坑笔记(3)- 并发,优雅关闭

    主函数也是线程 在不使用sync WaitGroup的情况下 需要main等待goroutine完成 不然main完成就没了 sync是synchronizing 使 同步 的缩写 wg done 最好加defer chan需要make c
  • Sqli-labs之Less-26和Less-26a

    Less 26 GET 基于错误 您所有的空格和注释都属于我们 根据提示我们知道这一关过滤了空格和注释 实际上过滤的远远不止这些 我们来看下源码 可以确认一下 id 231 确认过滤了 id or1 确认过滤了or id 1 确认过滤了多行
  • C语言 fstat函数

    系列文章目录 文章目录 系列文章目录 前言 一 stat系统调用 二 fstat 1 功能 2 相关函数 3 头文件 4 函数声明 5 描述 6 返回值 7 例子 三 struct stat结构体 前言 一 stat系统调用 stat系统调
  • Linux学习之expect操作详解

    一 expect安装介绍 1 expect命令安装 安装语句 yum install expect 2 expect命令含义 expect是一种脚本语言 它能够代替人工实现与终端的交互 主要应用于执行命令和程序时 系统以交互形式要求输入指定
  • Python全栈开发【基础-03】编程语言的分类

    专栏介绍 本专栏为Python全栈开发系列文章 技术包括Python基础 函数 文件 面向对象 网络编程 并发编程 MySQL数据库 HTML JavaScript CSS JQuery bootstrap WSGI Django Flas
  • usb鼠标驱动(一)

    Linux USB 鼠标驱动程序详解 注册一个usb driver 这个drvier不是usb设备driver 而是接口driver use a define to avoid include chaining to get THIS MO
  • 微信小程序消息订阅Java

    前言 编写日期 2022 11 04 写这篇文章原因 公司给政府做一个订餐系统 需要在员工在小程序上发起订餐后经过部门领导和书记的审批后 再由食堂确认订餐结果 在订餐审批单在各个节点流转的过程中 需要给每一个节点的审批人发送微信订阅消息和手
  • 【目标检测】目标检测的评价指标(七个)

    目录 目标检测的评价指标 一 正样本与负样本 二 真正 TP 假正 FP 真负 TN 假负 FN 1 正确的正向预测 True Positive TP 正样本被正确检测的数量 2 错误的正向预测 False Positive FP 3 错误
  • redis 过期消息订阅实现(java实现)

    一 开启redis消息通知功能 方法1 修改conf文件 编辑 etc redis redis conf文件 添加或启用以下内容 key过期通知 notify keyspace events Ex 方法2 使用命令 登陆redis cli
  • AUTOSAR-自旋锁(spinlock)与互斥锁

    AUTOSAR多核OS为实现核间资源互斥 保证数据一致性 设计了自旋锁机制 该机制适用于核间资源互斥 对于多核概念 需要一种新的机制来支持不同内核上任务的互斥 这种新机制不应在同一内核上的 TASK 之间使用 因为它没有意义 在这种情况下