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互斥体)的更多相关文章

  1. linux 内核的rt_mutex 锁操作实现的临界区

    rt_mutex 定义的锁规则: 以偶对齐的task_struct指针为上锁标记, 偶对齐的指针地址最低位用以标记是否有waiters. rt_mutex的trylock,lock,以及unlock都 ...

  2. Linux 内核网络协议栈 ------sk_buff 结构体 以及 完全解释 (2.6.16)

    转自:http://blog.csdn.net/shanshanpt/article/details/21024465 在2.6.24之后这个结构体有了较大的变化,此处先说一说2.6.16版本的sk_ ...

  3. linux 内核的futex pi-support,即pi-futex使用rt_mutex委托

    futex的pi-support,也就是为futex添加pi算法解决优先级逆转的能力,使用pi-support的futex又称为pi-futex.在linux内核的同步机制中,有一个pi算法的成例,就 ...

  4. 《Linux内核设计与实现》读书笔记(十)- 内核同步方法【转】

    转自:http://www.cnblogs.com/wang_yb/archive/2013/05/01/3052865.html 内核中提供了多种方法来防止竞争条件,理解了这些方法的使用场景有助于我 ...

  5. Linux内核同步

    Linux内核剖析 之 内核同步 主要内容 1.内核请求何时以交错(interleave)的方式执行以及交错程度如何. 2.内核所实现的基本同步机制. 3.通常情况下如何使用内核提供的同步机制. 内核 ...

  6. 初探内核之《Linux内核设计与实现》笔记上

    内核简介  本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核   原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单.2. 高效 ...

  7. Linux 内核协议栈 学习资料

    终极资料 1.<Understanding Linux Network Internals> 2.<TCP/IP Architecture, Design and Implement ...

  8. linux内核同步之每CPU变量、原子操作、内存屏障、自旋锁【转】

    转自:http://blog.csdn.net/goodluckwhh/article/details/9005585 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 一每 ...

  9. Linux内核中锁机制之完成量、互斥量

    在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...

随机推荐

  1. Java程序中与MongoDB建立连接~小记

    1.Mongo和MongoClient的关系 MongoClient继承自Mongo,使用Mongo也可建立连接,但是需要使用与Mongo适应的MongoOptions,MongoURI等类型. 2. ...

  2. Gson和Json

    一下内容为复制别人的: Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库.可以将一个 JSON 字符串转成一个 Java 对象,或者反过来. j ...

  3. Activity猫的一生-故事讲解Activity生命周期

    大家好,关于Android中Activity的生命周期,网上大多数文章基本都是直接贴图.翻译API,比较笼统含糊不清. 我就用故事来说一说: 有个人叫User,TA养了几只猫,有只猫叫Activity ...

  4. [SinGuLaRiTy] 平衡树

    [SinGuLaRiTy-1009] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 二叉查找树 二叉查找树是指具有下列性质的非空二叉树: ⑴ ...

  5. 网站防止SQL注入方法

    方法:所有获取GET.POST变量都先进行过滤: 字符串-- htmlspecialchars(addslashes($string))  addslashes() 函数返回在预定义字符之前添加反斜杠 ...

  6. C#重的数组、集合(ArrayList)、泛型集合(list<T>)三者比较及扩展延伸……

    本来我只想总结下数组.集合(ArrayList).泛型集合(list<T>)三者的比较的,可以一写下来要扩展的知识点有点多了,只能写一个小的知识点列表了如下: 1.数组.集合(ArrayL ...

  7. 爬虫入门系列(一):快速理解HTTP协议

    4月份给自己挖一个爬虫系列的坑,主要涉及HTTP 协议.正则表达式.爬虫框架 Scrapy.消息队列.数据库等内容. 爬虫的基本原理是模拟浏览器进行 HTTP 请求,理解 HTTP 协议是写爬虫的必备 ...

  8. SVN:重命名文件之后不允许提交

         提交文件所属的目录,这样可以提交成功.

  9. c++设计成员变量可动态调整的动态类结构

    本文主要介绍一下如何使用c++设计成员变量可动态调整的抽象动态类结构.首先介绍一下项目中以前使用的一种类结构:静态类结构 1.静态类结构 很多时候,在项目开发中设计类结构时,我们往往有一种简单.直接的 ...

  10. 【WCF】错误处理(四):一刀切——IErrorHandler

    前面几篇烂文中所介绍到的错误方式,都是在操作协定的实现代码中抛出 FaultException 或者带泛型参数的detail方案,有些时候,错误的处理方法比较相似,可是要每个操作协定去处理,似乎也太麻 ...