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

autosar中自旋锁和互斥锁的应用

配合使用的经典模式:嵌套锁(Nested Locking)
最常见的配合模式是先获取资源锁,再获取自旋锁。这个顺序至关重要,可以防止死锁。
操作流程
假设有两个任务:Task_A(高优先级)在Core 0上运行,Task_C(低优先级)在Core 1上运行,它们都要访问共享资源SharedData。
任务欲访问共享资源:
Task_A(Core 0)和Task_C(Core 1)都准备写入SharedData。
获取资源锁(核内同步):
Task_A 首先获取与SharedData关联的资源锁(Resource)。
由于SharedData此时未被本核其他任务占用,Task_A成功获取资源锁。它的优先级不会被提升(因为暂无冲突)。
Task_C在其核心上也成功获取了该资源锁。
获取自旋锁(核间同步):
Task_A然后获取与SharedData关联的自旋锁(Spinlock)。
Task_C也尝试获取同一个自旋锁。
假设Task_A更快一步,成功获取自旋锁。Task_C发现锁已被占用,于是在Core 1上开始“自旋”(忙等待),不断尝试获取锁。
访问临界区:
Task_A现在同时持有了资源锁和自旋锁,安全地进入临界区,对SharedData进行读写操作。
释放锁:
Task_A操作完成,先释放自旋锁。
正在自旋的Task_C立刻检测到自旋锁被释放,成功获取它。
Task_A然后释放资源锁。
Task_C现在持有了自旋锁,可以安全地访问SharedData。访问完成后,它按顺序先释放自旋锁,再释放资源锁。
顺序的重要性:为什么必须先Resource后Spinlock?
避免死锁:想象一下反过来的顺序。
Task_A(Core 0)先获得了自旋锁。
在它试图获取资源锁之前,被本核一个更高优先级的任务Task_B抢占。
Task_B也尝试访问SharedData,它先去获取资源锁。由于资源锁空闲,它成功获取。
Task_B接着尝试获取自旋锁,但自旋锁被Core 0上的Task_A持有。
此时,Task_B会因获取自旋锁失败而自旋(忙等待)。
但Task_A正在等待Task_B释放资源锁后才能继续运行… 死锁发生!
结论:总是先获取可能引起阻塞的锁(Resource),再获取不会引起阻塞的锁(Spinlock)。这个原则确保了在持有自旋锁(会禁用抢占、浪费其他核周期)之前,所有可能发生的阻塞行为都已经完成。

自旋锁(Spinlock)与互斥锁(Mutex)的区别与作用


核心区别
特性自旋锁(Spinlock)互斥锁(Mutex)
等待行为忙等待(Busy Waiting),持续循环检测锁状态阻塞等待(Sleep Waiting),线程进入休眠状态
CPU占用高(占用CPU时间片循环等待)低(释放CPU给其他线程)
适用场景临界区代码极短(纳秒级)、不可睡眠环境(如中断)临界区代码较长(微秒级以上)、可睡眠环境
实现复杂度低(依赖原子操作或硬件指令)高(依赖操作系统调度机制)
优先级反转风险高(可能因忙等待导致高优先级任务阻塞)低(通过优先级继承机制缓解)
中断上下文兼容性支持(需配合spin_lock_irqsave禁用中断)不支持(可能导致睡眠)

主要作用
自旋锁
  1. 极短临界区保护
    适用于多核系统中保护执行时间极短的共享资源操作(如计数器增减)。
  2. 不可睡眠环境
    用于中断上下文、原子上下文等禁止睡眠的场景(如硬件中断处理函数)。
  3. 低延迟要求
    避免线程切换开销,确保实时性(如内核调度器、网络协议栈)。
互斥锁
  1. 长临界区保护
    保护需要较长时间执行的共享资源操作(如文件I/O、复杂数据结构修改)。
  2. 可睡眠环境
    用于用户空间或内核中允许睡眠的上下文(如进程上下文)。
  3. 公平性保证
    通过操作系统调度实现公平锁获取(如FUTEX机制)。

代码实现对比
自旋锁(Linux内核示例)
#include <linux/spinlock.h>
spinlock_t my_lock;
spin_lock_init(&my_lock);// 加锁
spin_lock(&my_lock);
// 临界区操作...
spin_unlock(&my_lock);// 中断安全版本
unsigned long flags;
spin_lock_irqsave(&my_lock, flags);
// 临界区操作...
spin_unlock_irqrestore(&my_lock, flags);
互斥锁(Linux内核示例)
#include <linux/mutex.h>
struct mutex my_mutex;
mutex_init(&my_mutex);// 加锁
mutex_lock(&my_mutex);
// 临界区操作...
mutex_unlock(&my_mutex);

性能与场景选择
场景推荐锁类型原因
中断处理函数自旋锁中断上下文禁止睡眠
多核高频计数器操作自旋锁临界区极短,避免上下文切换开销
用户态多线程文件读写互斥锁临界区较长,允许线程休眠
内核模块长时间资源占用互斥锁避免CPU空转浪费

高级优化技术
  • 自旋锁优化
    • Ticket Spinlock:解决传统自旋锁的公平性问题(Linux 4.2+默认使用)。
    • MCS Lock:针对多核扩展性优化,减少缓存行争用。
  • 互斥锁优化
    • Adaptive Mutex:混合自旋与休眠(如Windows的CRITICAL_SECTION)。
    • Futex(Fast Userspace Mutex):用户态快速路径+内核态后备路径。

总结
  • 自旋锁:牺牲CPU资源换取低延迟,适用于极短临界区和不可睡眠环境。
  • 互斥锁:牺牲延迟换取CPU利用率,适用于长临界区和可睡眠环境。
    选择原则:优先使用互斥锁,仅在必要时(如中断上下文)使用自旋锁。

应用场景1:
我有个底层驱动的spi操作函数加了资源锁等待,如果别人在不同的task去运行我的代码会有问题吗?比如以下写法:

xx_vSetOut()
{
```cGetResource(OsResource_Spi);
...//这里执行spi操作ReleaseResource(OsResource_Spi);
}

然后在core1的task_10ms任务中以及core0的task_10ms中分别调用xx_vSetOut
结果影响:

注意:
在多核系统中,同一个资源锁能否被分配给两个核?
不能。资源锁(如互斥锁、自旋锁)的核心设计目标是保证互斥访问,即同一时间只能有一个执行单元(核/线程)持有锁。若锁被分配给核0,核1的任务尝试获取该锁时会被阻塞,直到锁被释放。

核1的任务尝试获取核0持有的锁会导致的问题

  1. 阻塞与性能问题
    自旋锁(Spinlock):核1会持续轮询锁状态(忙等待),导致CPU资源浪费,可能引发高负载下的性能瓶颈。
    互斥锁(Mutex):核1的任务会被挂起(进入睡眠状态),触发上下文切换,增加延迟。
  2. 死锁风险 若核0因中断、优先级反转或逻辑错误未及时释放锁,核1将无限期等待,导致系统僵死。 若锁的持有者(核0)在释放锁前被抢占或崩溃,锁可能永远无法释放。
  3. 优先级反转 若核1的任务优先级高于核0的任务,但核0因持有锁而阻塞核1,会导致高优先级任务被低优先级任务“卡住”。
  4. 数据竞争与不一致性 若锁未正确使用(如未保护共享资源),核1可能绕过锁直接访问资源,导致数据损坏或逻辑错误。

锁的“分配”机制澄清 锁不属于特定核:锁的持有者是线程/任务,而非物理核。核0的线程释放锁后,核1的线程可获取。
锁的公平性:某些锁(如Ticket Lock)通过队列机制保证先到先得,避免饥饿问题。
锁的合理使用:
确保锁的获取和释放严格配对。
避免在持有锁时执行耗时操作(如I/O、复杂计算)。
锁类型选择:
短临界区用自旋锁(避免上下文切换开销)。
长临界区用互斥锁(避免CPU空转)。
死锁预防:
避免嵌套锁(Lock Hierarchy)。
使用超时机制(如pthread_mutex_timedlock)。

随着使用经验待继续完善。。。

http://www.dtcms.com/a/393523.html

相关文章:

  • 建筑可视化告别“假”渲染:用Photoshop+Twinmotion打造照片级场景
  • 一键生成linux服务器健康巡检html报告
  • 数据结构(C语言篇):(十八)交换排序
  • Ubuntu20.04下跑通ORB-SLAM2
  • C++二进制转十进制
  • WordPress用户系统 + JWT认证:打造统一的应用登录解决方案
  • PortSwigger靶场之将反射型 XSS 注入到带有尖括号和双引号的 JavaScript 字符串中,并使用 HTML 编码和单引号进行转义通关秘籍
  • win11电脑按键失灵,提供几个可能恢复的方法
  • Android 中获取稳定时间的方法
  • mac编译ffmpeg
  • Deepsoil V7.1.10+Shake2000,最新版程序、教学视频、PDF使用手册
  • Apollo相机数据RTMP推流与播放指南
  • 使用Python扩展Unity编辑器
  • 【Android】自定义控件
  • 探索 Event 框架 6:高级特性与性能优化
  • JavaSE基础——第九章 枚举类注解
  • 云计算在金融领域中的应用
  • 【入门算法】前缀和:先预存再求和,以空间换时间
  • mac编译vst3sdk
  • Java 网络原理(二)--- TCP的机制 + IP协议 +以太网协议 + DNS
  • Python文件名编码处理深度解析:绕过编码问题的专业指南
  • 如何利用简单的浏览器插件Web Scraper爬取知乎评论数据
  • 鹿鼎记豪侠传:Rust 重塑 iOS 江湖(下)
  • 03.博客版-镜像
  • 云原生docker离线二进制安装
  • RabbitMQ 的配置文件位置及常见配置项
  • Visual Prompt Builder-AI 提示词可视化工具
  • 一文掌握Flask:从基础使用到高级应用
  • 23种设计模式之【责任链模式】-核心原理与 Java 实践
  • 执行 conda update -n base -c defaults conda 后仍提示需要升级