linux 内核的rt_mutex (realtime互斥体)
linux 内核有实时互斥体(锁),名为rt_mutex即realtime mutex。说到realtime一定离不开priority(优先级)。所谓实时,就是根据优先级的不同对任务作出不同速度的响应。rt_mutex也就是依据任务(task,process)的priority进行排队的锁,同时使用PI(priority Inheritance,优先级继承)算法解决proirty inversion(优先级逆转)的问题。
这里有三个角色:rt_mutex,rt_mutex_waiter,以及task_struct。(pi算法与进程结构偶合在一起)
rt_mutex:实时锁,使用优先级排队,使用优先级继承算法解决优先级逆转问题,与task_struct强偶合。
task_struct:要求锁的任务,rt_mutex用其指针作为上锁状态。
rt_mutex_waiter:任务阻塞等待锁,即任务与锁的关联,代表任务对锁的一次需求。
rt_mutex和task_struct都有一个waiter有序队列,并且都实现为rb树,并以优先级prio进行排序。rt_mutex_waiter->tree_entry入队到rt_mutex(锁)的waiter队列,即rt_mutex->waiters。而rt_mutex_waiter->pi_tree_entry入队到task_struct(任务)的waiter队列,即task_struct->pi_waiters。
rt_mutex->waiters:用于阻塞等待排队,与pi chain无关。
task_struct->pi_waiters:并非用于排队,而是作为一个最小值计算器,为task_struct计算可继承的最小prio(最高),并且提供一个是否为空的判断属性。它收集了task持有的所有rt_mutex的leftmost_waiter,即每当一个task在一个rt_mutex上了锁,这个rt_mutex的waiters中prio最小(最高)的waiter将被入队到task_struct->pi_waiters。与pi chain无关。
rt_mutex_waiter->prio:rt_mutex->waiters和task_struct->pi_waiters两个队列都使用这个值进行排序。一个在pi chain上阻塞的任务,当优先级被boost后,同样会反映在waiter结构的prio上。
pi chain:从一个任务的task_struct->pi_block_on开始,依次进行rt_mutex = rt_mutex_waiter->lock,task_struct = rt_mutex->owner,rt_mutex_waiter = task_struct->pi_block_on访问遍历。这个链并非我们通过指的用链表结构进行链接的链表。而是 task -> waiter -> lock -> ... 的chain。虽然rt_mutex->waiters 和 task_struct->pi_waiters 同为队列(由rbtree实现),但是跟pi chain无关,pi chain walk路径不通过这两个队列。
以内核的设计文档rt-mutex-design.txt举例,有pi chain如下
Example: Process: A, B, C, D, E
Mutexes: L1, L2, L3, L4 A owns: L1
B blocked on L1
B owns L2
C blocked on L2
C owns L3
D blocked on L3
D owns L4
E blocked on L4 The chain would be: E->L4->D->L3->C->L2->B->L1->A
在上面的例子中,A为最终的阻塞源,它继承了阻塞链上所有其它任务的优先级。图左则的任务被右则的任务阻塞,图右则的任务持有锁并继承图左则的任务的优先级。当一个任务被一个锁阻塞,并且这个锁在图上。以任务E为例,当E加入到pi chain后,就会从图的左则向图的右则进行pi chain walk,直到到达任务A,在walk所经节点,都会将优先级作最优继承。通常地pi chain由图最右则的A释放锁,一路唤醒pi chain上阻塞的任务,而消减pi chain。A首先离开pi chain,由于继承了优先级,必须马上恢复原有的优先级,但是并不需要进行pi chain walk,因为它是继承者不是继承源(没有pi_block_on),不影响其它继承者(pi chain上的任务)的继承。 但是如C,超时不阻塞了,要离开pi chain,因为它不但是继承者还是继承源(pi_block_on -> B),所以C必须对B进行pi chain walk。
下面是进行walk chain的调用拓扑,rt_mutex_adjust_prio_chain执行pi chain walk。
pi算法解决优先级逆转问题。
高(high)优先级任务由于等待低(low)优先级任务拥有的锁,在这过程中低优先级被其它优先级(界于高低之间的中级medium)的任务无限抢占,从而有机会释放锁,导致高优先级的任务无限等待。
下面设两轴,水平轴为时间,纵轴为优先级。
当一个high prio任务进来,抢占一个low prio任务后却发现有资源要同步又阻塞了,而这个同步的资源low prio任务正在使用,那么必须等待low prio任务释放掉同步的资源,high prio任务才能再次运行。但是有一种坏的情况是,low prio任务被medium prio任务抢占,不能及时释放同步的资源,使用high prio任务阻塞时间增加。
这还不算最坏的,最坏的是low prio任务被不定数目或不定回目的medium prio任务无限地进行抢占,最后导致high prio任务无限地被阻塞。
解决的方法就是,当high prio任务阻塞在一个同步资源时,将正在使用同步资源的任务临时提升 (boost)跟自己一样的优先级,赶紧释放资源,好让(方便)high prio任务通过。
rt_mutex->waiters 和 task_struct->pi_waiters 如何参与pi算法。
前面已经谈及的pi chain,也是pi算法的一部分。而pi算法的另一部分就是如何选出最高的优先级进行继承。这是通过rt_mutex->waiters和task_struct->pi_waiters队列来实现的。每当一个任务持有一个rt_mutex时,就会从rt_mutex->waiters中选出排在首位的waiter纳入到自己的task_struct->pi_waiters队列,这个同样是一个排序队列。这个队列代表着任务持有哪些rt_mutex,并且以及阻塞在这些rt_mutex的最高优先级(的任务)。这个队列将这些优先级进行排序选出最高的优先级进行继承。
一个对rt_mutex的lock或者unlock都可能会引发rt_mutex->waiters队列的入队和出队,从而改变了rt_mutex->waiters的排序,继而改变了task_struct->pi_waiters队列的成员,必须重新排序,选择新的优先级来继承。当pi chain上的一个任务的优先级继承受影响后,就必须要反映在它所阻塞的rt_mutex的waiters队列上,并按pi chain阻塞的方向传递变化。
任务 P 持有两个锁 L1 和 L2。A,B,C 阻塞在 L1;而 C,D,F 阻塞在 L2。 B 和 F 分别是 L1 和 L2 的 leftmost waiter,以最好的优先级 1 和 0,被任务 P 选中放在自己的 pi_waiters 队列。排序后,F 的优先级最高,作为继承的优先级。
当一个优先级更高的任务 X 需要阻塞在锁 L1 时,必然引发锁 L1的leftmost waiter改变,这时就要通知 P 选择 X 代替 B,重新对比 X 和 F 的优先级,选最高的优先级来继承。如果任务 P 还阻塞等待一个锁 L3 ,那么就必须进行 pi chain walk,通知优先级继承变更。
一个阻塞任务从其task_struct->pi_block_on开始进行 pi chain walk 通知优先级继承变更。
一个任务释放锁,从其task_struct->pi_waiters队列找出锁对应的任务进行唤醒。
linux 内核的rt_mutex (realtime互斥体)的更多相关文章
- linux 内核的rt_mutex 锁操作实现的临界区
rt_mutex 定义的锁规则: 以偶对齐的task_struct指针为上锁标记, 偶对齐的指针地址最低位用以标记是否有waiters. rt_mutex的trylock,lock,以及unlock都 ...
- Linux 内核网络协议栈 ------sk_buff 结构体 以及 完全解释 (2.6.16)
转自:http://blog.csdn.net/shanshanpt/article/details/21024465 在2.6.24之后这个结构体有了较大的变化,此处先说一说2.6.16版本的sk_ ...
- linux 内核的futex pi-support,即pi-futex使用rt_mutex委托
futex的pi-support,也就是为futex添加pi算法解决优先级逆转的能力,使用pi-support的futex又称为pi-futex.在linux内核的同步机制中,有一个pi算法的成例,就 ...
- 《Linux内核设计与实现》读书笔记(十)- 内核同步方法【转】
转自:http://www.cnblogs.com/wang_yb/archive/2013/05/01/3052865.html 内核中提供了多种方法来防止竞争条件,理解了这些方法的使用场景有助于我 ...
- Linux内核同步
Linux内核剖析 之 内核同步 主要内容 1.内核请求何时以交错(interleave)的方式执行以及交错程度如何. 2.内核所实现的基本同步机制. 3.通常情况下如何使用内核提供的同步机制. 内核 ...
- 初探内核之《Linux内核设计与实现》笔记上
内核简介 本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核 原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单.2. 高效 ...
- Linux 内核协议栈 学习资料
终极资料 1.<Understanding Linux Network Internals> 2.<TCP/IP Architecture, Design and Implement ...
- linux内核同步之每CPU变量、原子操作、内存屏障、自旋锁【转】
转自:http://blog.csdn.net/goodluckwhh/article/details/9005585 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 一每 ...
- Linux内核中锁机制之完成量、互斥量
在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...
随机推荐
- 构建微服务(Building Microservices)-PDF 文档
闲时翻译了几篇基于Spring Cloud.Netflix OSS 构建微服务的英文文章,为方便分享交流,整理为PDF文档. PDF 文档目录: 目录 一.微服务操作模型... 3 1. 前提 ...
- Eclipse集成Tomcat教程
(初学者都会问一个问题,就是Eclipse好用还是Myeclipse好用.好吧,这个问题我昨晚才刚刚问完,哈哈,因为我一开始学Java都是直接下了一个MyeClipse来用的,没想过太多.其实也是,两 ...
- JavaEE开发之Spring中的依赖注入与AOP
上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...
- Unity 3D Framework Designing(1)—— MVVM 模式的设计和实施(Part 2)
MVVM回顾 经过上一篇文章的介绍,相信你对 MVVM的设计思想有所了解.MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在.View只关心怎样渲染,而ViewModel只 ...
- css3渐变生成器网页
http://westciv.com/tools/gradients/ http://www.colorzilla.com/gradient-editor/
- 走入PHP-初次见面
FROM:实验楼 Linux启动WEB服务器: $ sudo service apache2 start 新建并打开test.php文件: $ sudo gvim /path/test.php PHP ...
- Java之路——环境配置与编译运行
本文大纲 一.开篇 二.JDK下载 三.JDK安装 四.环境配置 五.初识Java编译 六.Java与Javac 七.第一个Java程序 八.总结 九.参考资料 一.开篇 通过对之前Java之路的了解 ...
- 2017年要学习的JavaScript的顶级框架和主题
JavaScript的流行促进了一个非常活跃的由相关技术,框架和库组成的生态圈的发展.整个生态圈的多样性和活跃性越来越强,这让许多人变得越来越困惑. 你应该了解些什么技术呢? 我们应该将时间花费在 ...
- linux 下日常使用便利工具
Nautilus 你工作中有在GUI和命令行之间切来切去吗?当你总是要在命令行中输入你要进入的目录的时候,你有沮丧无奈过吗?如果有的话,那么,你一定要试下这个nautilus插件 —— nautilu ...
- div背景图片或颜色不显示的解决办法
背景图片不显示的原因: 1. css没有被调用 2. css图片地址不对 3. div的高度没有固定,是auto.没有设值或者高度不够 4. div被嵌套 5. div代码不规范 解决办法: (1)D ...