再解析下内核自旋锁和优先级翻转问题

[内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析

漫画|Linux 并发、竞态、互斥锁、自旋锁、信号量都是什么鬼?

Linux内核自旋锁

之前写的自旋锁的文章,现在再加一篇,可能单纯的一两次说明不能把问题说清楚。所以再写一篇文章,也希望更多的人参与讨论,这样会让问题更加清晰明了。

自旋锁的特点是在等待锁的过程中不会休眠,会不断的占用CPU轮询锁的状态,一旦发现锁被释放,就会马上获取锁。 基于这样的特点,自旋锁spinlock适用于保护执行时间非常短的临界区。

自旋锁有两个特点

  • 进入临界区后不能调用可能引起系统休眠的函数。

  • 临界区的代码不能被中断函数重入调用。

如果进入临界区后睡眠,会引起这样的问题,如下图

如果临界区的代码在执行的时候,中断重入调用,如下图

上面两种情况下,都出现一个问题,就是在临界区运行时,还没有来得及释放锁,当前进程被动释放了CPU的使用权,然后下次「可能是中断处理函数,可能是CPU调度的其他进程」再进来的时候,情况就会比较复杂,因为之前的程序一直没有释放,导致锁一直获取失败,失败后又一直在等待,而且永远等不到锁的释放,就会导致死锁了。

优先级反转问题

系统运行对时间要求非常严格,如果因为某些问题导致系统时间延迟有误差,可能会导致比较严重的问题,这种情况在实时系统中会更严重。

我描述下优先级反转的问题。

A和C共享一个资源,但是在运行过程中,在某一个时刻,C占有资源的时候,被高于它优先级的进程B抢占了,这时候B就处于一个有利位置,一直会有CPU运行,如果有其他进程优先级高于C的,也会能拿到CPU运行。

这就出现了一个奇怪的现象,低优先级的进程抢占了高优先级的进程,如果A是特斯拉的刹车进程的话,我相信故障就此发生。

如何解决优先级翻转的问题呢?

  • 提升C的优先级,让C的优先级高于B,就不会存在持有锁的情况下被抢占。

但是C的优先级提升到多少合适呢?

假设共享资源R,有5个任务会申请它,我们需要做的是,持有R资源的任务的优先级是这5个任务中最高的,这就叫优先级提升

spinlock相关代码,基于4.4内核

typedef struct {volatile unsigned int slock;
} arch_spinlock_t;#define __ARCH_SPIN_LOCK_UNLOCKED__ 0
#define __ARCH_SPIN_LOCK_LOCKED__ 1#define __ARCH_SPIN_LOCK_UNLOCKED { __ARCH_SPIN_LOCK_UNLOCKED__ }
#define __ARCH_SPIN_LOCK_LOCKED  { __ARCH_SPIN_LOCK_LOCKED__ }
//...typedef struct raw_spinlock {arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAKunsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCKunsigned int magic, owner_cpu;void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lockdep_map dep_map;
#endif
} raw_spinlock_t;//...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;

自旋锁函数调用

static __always_inline void spin_lock(spinlock_t *lock)
{raw_spin_lock(&lock->rlock);
}
//1==================
#define raw_spin_lock(lock) _raw_spin_lock(lock)
//2==================
#ifndef CONFIG_INLINE_SPIN_LOCK
void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
{__raw_spin_lock(lock);
}
EXPORT_SYMBOL(_raw_spin_lock);
#endif
//3==================
static inline void __raw_spin_lock(raw_spinlock_t *lock)
{preempt_disable();spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}
//4==================LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
//5==================
#define LOCK_CONTENDED(_lock, try, lock)   \
do {        \if (!try(_lock)) {     \lock_contended(&(_lock)->dep_map, _RET_IP_); \lock(_lock);     \}       \lock_acquired(&(_lock)->dep_map, _RET_IP_);   \
} while (0)//6==================
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{__acquire(lock);arch_spin_lock(&lock->raw_lock);
}//7==================static inline void arch_spin_lock(arch_spinlock_t *lock)
{unsigned int val;SCOND_FAIL_RETRY_VAR_DEF;smp_mb();__asm__ __volatile__("0: mov %[delay], 1  \n""1: llock %[val], [%[slock]] \n" /*LOCK指令前缀会设置处理器的LOCK#信号(译注:这个信号会使总线锁定,阻止其他处理器接管总线访问内存),直到使用LOCK前缀的指令执行结束,这会使这条指令的执行变为原子操作。在多处理器环境下,设置LOCK#信号能保证某个处理器对共享内存的独占使用。*/" breq %[val], %[LOCKED], 0b \n" /* spin while LOCKED 判断变量是否为0,如果不为0,说明自旋锁已经被获取,当前获取就会失败 */" scond %[LOCKED], [%[slock]] \n" /* acquire */" bz 4f   \n" /* done */"     \n"SCOND_FAIL_RETRY_ASM: [val]  "=&r" (val)SCOND_FAIL_RETRY_VARS: [slock] "r" (&(lock->slock)),[LOCKED] "r" (__ARCH_SPIN_LOCK_LOCKED__) /*获取锁,把变量值加1*/: "memory", "cc");smp_mb();
}

参考

  • https://blog.csdn.net/longwang155069/article/details/52055876


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

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

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

相关文章

有许多话不能说

说的问题是一个大问题。人,长了嘴巴,除了吃饭便是说话。当然还有代替鼻孔呼吸、排除呼吸道污秽等功能,但均不可与说同日而语。涉及说,就有说的内容问题,据观察,有许多话是不能说的。 好话不能说&#xff0c…

ios 逆向编程(环境搭建)

首先如果你想要逆向其他的APP 动态的查看 或者修改人家APP里面的东西 1, 首先要有一台越狱的手机 最好是9.1以下的,因为9.2以上(包括9.2)就不能完美越狱了 2,手机也要5s以上的(因为从5S开始支持arm64架构&…

嵌入式OS入门笔记-以RTX为案例:三.初探进程

嵌入式OS入门笔记-以RTX为案例:三.初探进程1.理论 进程,英文称呼很多Process, Task 等等,一般通用操作系统称Process的比较多,各种称呼涵义稍微有不一样。一般而言,进程是对一个运行单元的抽象,主要包括…

图文方式管理Linux服务器(Webmin)

客户让做webmin,才知道linux下也有界面的管理了 对于大部分使用计算机的人来说,linux的印象就是一大堆的命令字符以及黑黑的显示屏。虽然现在Linux的桌面版有了长足的进步,界面已和Windows不相上下了.但对于Linux服务器来说&am…

最大、最小堆的实现

最大最小堆 堆是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于(或不小于)其左子节点和右子节点的值。 最大堆和最小堆是二叉堆的两种形式。 最大堆:根结点的键值是所有堆结点键值中最大者。 最小堆:根…

嵌入式OS入门笔记-以RTX为案例:四.简单的时间管理

嵌入式OS入门笔记-以RTX为案例:四.简单的时间管理 上一节简单记录了进程task。有了进程以后,我们需要关心怎么样分配CPU资源(或者运行时间)给每个进程。那么就要引入排程(scheduling)的概念。排程一般都是O…

我等这个含蓄的技术男当上了CEO

大家好,祝大家五一节日快乐!今天没有写技术文章,今天想吹一个人,他是我的朋友,他做公众号很久了,技术文章写的也不错,但是阅读和关注量一直没有上来,我之前好几次在公众号上转发了他…

Oracle的列转行问题

Oracle的列转行问题Oracle中使用语句将行数据转换称不同的列表示,或者将不同的列数据写到同一列的不同行上的行列转换问题是一个非常传统的话题。网络上流传了很多将行数据转换称列数据的方法和应用实例,一般通过decode或者case函数与聚合函数联合实现功…

Linux CAN通信

Linux CAN通信 实现了Linux下的CAN通信——初始化&#xff0c;发两个送和接收&#xff08;采用队列形式&#xff09;&#xff0c;使用两个线程&#xff0c;还有一个超时响应目前未写。接收部分使用select实现。 #ifndef _CAN_H_ #define _CAN_H_#include <stdio.h> #incl…

(四)Kinect人脸识别

kinect可以通过摄动摄像头不仅可以获取人脸位置旋转信息&#xff0c;也可以获取脸部轮廓的三维坐标 可以参考插件中的场景KinectFaceTrackingDemo1-4&#xff0c;在kinectManager基础上需要脚本FacetrackingManager。 1&#xff09;通过KinectManager kinectManager KinectMan…

广东总冠军

lets go tiger 看这篇文章之前&#xff0c;先看看我们看球的视频 恭喜广东拿下总冠军&#xff01; 恭喜胡明轩夺得FMVP&#xff01; 我当时预测的是周鹏或者胡明轩拿下FMVP&#xff0c;最后是胡明轩&#xff0c;广东后场三条枪表现都非常亮眼。如果是上一场广东夺冠&#xff0c…

Spring切入点表达式常用写法

Spring切入点表达式常用写法自从使用AspectJ风格切面配置&#xff0c;使得Spring的切面配置大大简化&#xff0c;但是AspectJ是另外一个开源项目&#xff0c;其规则表达式的语法也稍稍有些怪异。下面给出一些常见示例的写法&#xff1a;比如&#xff0c;下面是一个对Service包上…

每日一题(1) —— 数组计算

判断下面代码是否可执行&#xff1f;如果可执行&#xff0c;执行结果是多少&#xff1f; #include <stdio.h>int main(void) {int array[10] {0, 2, 3, 4, 5, 6, 7, 8, 9, 10};0[array] 1;printf("%d\n", (-1)[array 5]);return 0; } 分析&#xff1a; C语…

SQLAlchemy Script

SQLAlchemy: 1.由于sqlalchemy中没有提供choice方法&#xff0c;所以借助SQLAlchemy-Utils组件提供的choice方法 from sqlalchemy_utils import ChoiceType Base declarative_base() class Xuan(Base): __tablename__ xuan types_choices ( (1,欧美), (2,日韩), (3,老男孩),…

内存文章汇总,并剖析mmap

在看这篇文章之前&#xff0c;可以先看看下面这几篇文章Linux内存&#xff0c;先看这篇文章Linux内存寻址方式Linux虚拟内存TLBLinux物理内存初始化Linux io内存存在的意义~修改cmdline 把内存改成512MB用mtrace定位内存泄漏什么是内存泄漏&#xff1f;Linux内存管理slub分配器…

[综述泛读] A survey on web services composition (IJWGS, 2005)

Time: 2.5 hours Dustdar S, Schreiner W. "A survey on web services composition." International Journal of Web and Grid Services: 1-30. 2005 (30 pages, 单栏) (gs:169) Schahram Dustdar (维也纳技术大学, full prof) Dusdar是Distributed Systems Group的老…

Spring 事务与脏读、不可重复读、幻读

索引&#xff1a; 目录索引 参看代码 GitHub&#xff1a; 1.Spring 事务 2.事务行为 一、Spring 事务: Spring 的事务机制是用统一的机制来处理不同数据访问技术的事务处理。 Spring 的事务机制提供了一个 PlatformTransactionManager 接口&#xff0c;不同的数据访问技术的事务…

韦老师的开发板和嵌入式书籍赠送

大家五一快乐&#xff01;我知道这个时候大家都没有什么心思学习&#xff0c;所以找了联合了几个朋友一起给大家送点东西。这几个技术号主都非常用心的给大家分享技术文章&#xff0c;我相信&#xff0c;跟他们一起&#xff0c;你们也能变得更加优秀。奖品包括&#xff1a;1. 韦…

每日一题(2)—— -2与2的比较

分析下面的代码&#xff0c;求运行结果。 #include <stdio.h>int main(void) {if(-2 > 2){printf("11111\r\n");}else{printf("22222\r\n");}return 0; }分析&#xff1a; -2和2都没有声明存储类型&#xff0c;编译器默认按int存储&#xff0c;所…

正则表达式之道

正则表达式之道 原著&#xff1a;Steve Mansour smanscruznet.com Revised: June 5, 1999 (copied by jm /at/ jmason.org from , after the original disappeared! ) 翻译&#xff1a;Neo Lee neo.leegmail.com 2004年10月16日 英文版原文 译者按&#xff1a;原文因为年代久远…