linux实时进程优先级rt,Linux实时性- PREEMPT_RT实时抢占实现

作者:Paul E. McKenney

翻译整理:土豆丝624

原文链接:

概述:

本篇文章主要讲Linux的实时包PREEMPT_RT 是如何实现的。

PREEMPT_RT 的原理

PREEMPT_RT包的关键点是要使非抢占式的内核代码量尽可能的少,同时为了提供抢占性而必须修改的代码量也要尽可能的少。特别是临界区,中断处理程序和中断禁用的代码序列通常是可抢占式的。PREEMPT_RT包充分利用Linux内核的SMP能力来增加额外的抢占能力,而不是重写Linux内核。某种程度上,可以认为是抢占是给系统新加了一个CPU,然后使用常规锁定原语与抢占任务采取的任何操作进行同步。

注意:这里说的一些原理不要从字面意思上去理解。比如PREEMPT_RT包对每个抢占并不是一个热拔插事件。关键点是使用抢占必须提供SMP机制。后面的章节会详细介绍如何应用这些原理。

PREEMPT_RT的功能

PREEMPT_RT包有如下特性

抢占式临界区

抢占式中断处理

抢占式中断禁止代码序列

内核自旋锁和信号量的优先级继承

递延操作

降低延迟的措施

抢占式临界区

在PREEMPT_RT中,普通的自旋锁(spinlock_t and rwlock_t)是抢占式的,RCU读取侧临界区((rcu_read_lock() 和rcu_read_unlock())也是一样的。信号量临界区是可抢占的,他们已经存在于可抢占和非抢占内核中。这种可抢占性意思是可以阻止获取自旋锁,也就是在可抢占或中断禁用的情况下获取自旋锁是非法的(这个原则的一个例外就是变体_trylock,只要不是在密集信号中重复调用)。这也意味着当使用spinlock_t的时候spin_lock_irqsave()不会禁用硬件中断。

问题1:如何在非抢占内核中实现抢占式信号量临界区?

在中断或抢占禁用的情况下要获取锁要做什么?用raw_spinlock_t而不是spinlock_t,调用spin_lock()的时候使用raw_spinlock_t。PREEMPT_RT包含一个宏集合,这样会让spin_lock()调用的时候就像c++中的函数重载。当使用raw_spinlock_t的时候,就是传统的自旋锁。但是当使用spinlock_t,临界区就是可抢占的。当使用raw_spinlock_t时,各种_irq原语(例如spin_lock_irqsave())会禁用硬件中断,而在使用spinlock_t时不会禁用硬件中断。但是,使用raw_spinlock_t(及其对应的rwlock_t,raw_rwlock_t)应该是例外,而不是常规使用。在一些底层区域比如调度,特定的架构代码和RCU,是不需要这些原始锁的。

自从临界区可以被抢占,就不能依赖单个CPU上给定的临界区。因为是可抢占的,所以可能会移到其他的CPU上。所以,当你在临界区使用per-CPU变量时,必须单独处理抢占的可能性。因为spinlock_t和rwlock_t不再具有这个功能。

可以通过以下两种方式实现。

1. 显示禁用中断,或者通过调用get_cpu_var(), preempt_disable(),或者禁掉硬件中断

2. 使用per-CPU锁来保护per-CPU变量,可以通过使用新的DEFINE_PER_CPU_LOCKED()原语。

由于spin_lock可以睡眠,所以会增加一个额外的任务状态。思考一下下面的代码序列

spin_lock(&mylock1);

current->state = TASK_UNINTERRUPTIBLE;

spin_lock(&mylock2); // [*]

blah();

spin_unlock(&mylock2);

spin_unlock(&mylock1);

由于第二个spin_lock()调用可以睡眠,所以有可能会改变current-state的值,有可能使函数blah()产生令人惊讶的结果。在这种情况下,调度程序可以使用新的TASK_RUNNING_MUTEX位来保留current-state之前的值。尽管生成的环境有点陌生,但是通过少量的代码改动就实现了临界区抢占,并且PREEMPT_RT, PREEMPT和 non-PREEMPT三个配置项都是用相同的代码。

抢占式中断处理

在Preempt_RT环境中几乎所有的进程上下文都有中断处理。虽然任何标为SA_NODELAY的中断都可以在其上下文中运行,但是仅在fpu_irq, irq0, irq2和lpptest指定了SA_NODELAY。其中,只有irq0(per-CPU计时器中断)可以正常使用。fpu-irq是用于浮点协处理器中断,而lpptest是用于中断等待时间基准测试。注意软件计时器(add_timer())不在硬件上下文中运行。它是运行在进程上下文中,并且是完全抢占式的。

注意不要轻易使用SA_NODELAY,因为它会大大降低中断和调度延迟。Per-CPU计时器中断之所以符合条件,是因为它与调度程序和其他核心内核组件紧密相关。此外,在写SA_NODELAY中断处理代码的时候必须要非常谨慎,否则很容易出现崩溃和死锁。

由于per-CPU计时器中断运行在硬件中断上下文中,因此任何和进程上下文代码共享的锁必须是原始自旋锁(raw_spinlock_t 或 raw_rwlock_t)。并且,从进程上下文获取时,必须使用_irq变体,比如spin_lock_irqsave()。另外,当进程上下文代码访问每个和SA_NODELAY中断处理程序共享的per-CPU变量的时候,一般上要禁用硬件中断。

抢占式“中断禁用”代码序列

抢占式中断禁用代码序列的概念从术语上理解似乎是矛盾的,但是牢记PREEMPT_RT原理很重要。原理就是要依靠Linux内核的SMP功能来处理和中断处理程序的竞争。大多数中断处理程序都运行在进程上下文中。任何与中断处理程序有交互的代码都要准备处理在其他CPU上同时运行的该中断处理程序。

因此,spin_lock_irqsave()和相关的原语不需要禁用抢占。之所以安全的原因是,即使中断处理程序运行,即使它抢占了拥有spinlock_t的代码,但是在试图获取spinlock_t的时候会立即阻塞。临界区依旧会被保留。

但是,local_irq_save()依旧禁用抢占,因为没有任何锁依赖它。因此使用锁而不是local_irq_save()可以降低调度延迟,但是以这种方式替换锁会降低SMP性能,因此要小心。

需要和SA_NODELAY中断交互的代码不能使用local_irq_save(),因为它没用禁用硬件中断。相反,应该使用raw_local_irq_save(),类似的,当需要和SA_NODELAY中断处理程序交互的时候,需要使用原始自旋锁(raw_spinlock_t, raw_rwlock_t 和raw_seqlock_t)。但是原始自旋锁和原始中断禁用不应该在一些底层区域,如调度程序,架构依赖代码和RCU之外使用。

内核自旋锁和信号量的优先级继承

实时程序员会经常担心优先级倒置,这可能会发生一下几种情况:

低优先级任务A获取资源,比如获取锁

中优先级任务B开始执行CPU绑定,抢占低优先级任务A

高优先级任务C试图获取低优先任务A持有的锁,但是被阻塞了。因为中优先级任务B已经抢占了低优先级任务A

这种优先级倒置可以无限期地延迟高优先级任务。有两种方式可以解决这个问题:(1)抑制抢占;(2)优先级继承。第一种情况,由于没有抢占,所以任务B不能抢占任务A,从而阻止优先级反转的发生。这种方式在PREEMPT内核中用于自旋锁,但不用于信号量。抑制抢占对于信号量来说是没有意义的。因为持有一个信号量的时候阻塞是合法的,即使没有抢占也会导致优先级反转。对于某些实时工作负载,自旋锁也不能抑制抢占,因为会对调度延迟造成影响。

优先级继承可以用在抢占抑制没有意义的场合。就是高优先级任务临时把优先级赠与持有关键锁的低优先级任务。优先级继承是可以传递的:在上面的例子中,如果更高优先级任务D试图获取高优先级任务C已经持有的第二把锁,任务C和A都将暂时提升为任务D的优先级。优先级提升的持续时间也受到严重限制:一旦低优先级任务A释放了锁,它会立刻失去临时提升的优先级,把锁交给任务C。

但是,任务C运行需要时间,很可能同时另一个更高优先级任务E来试图获取锁。如果发生这种情况,任务E会从任务C那里偷到锁。这样是合法的,因为任务C还没有运行,因此实际上它并没有获取锁。另一方面,如果任务C在任务E试图获取锁之前已经运行,那么任务E是无法偷锁的,必须等待任务C释放锁,可能会提高任务C的优先级以加快处理速度。

另外,在某些情况下会长时间保持锁定。其中一些增加了“抢占点”,以便锁持有者在某些其他任务需要时丢弃该锁。

事实证明,读写优先级继承特别成问题。因此,尽管任务可以递归获取,但Preempt_RT可以通过一次只允许一个任务获取读写锁或信号量来简化这个为题。尽管限制了可扩展性,但这让优先级继承实现成为可能。

问题2:实现读写优先级继承的简单快捷的方法是什么

此外,在某些情况下,信号量不需要优先级继承,比如当信号量用于事件机制而不是锁的时候。compat_semaphore 和compat_rw_semaphore变体可以用于这种情况。很多信号量原语(up(), down()等)可用于compat_semaphore 和compat_rw_semaphore。相同的,读写信号量原语(up_read(), down_write()等)可用于compat_rw_semaphore 和rw_semaphore。

总结一下,优先级继承可以防止优先级反转,允许高优先级任务及时获取锁和信号量,即使这些锁和信号量被低优先级任务持有。PREEMPT_RT的优先级继承具有传递性且能够及时移除,并且具有当高优先级任务突然需要低优先任务持有的锁时,处理这种情况的灵活性。当信号量用于事件机制的时候,compat_semaphore 和compat_rw_semaphore可以避免优先级继承。

递延操作

由于spin_lock()现在可以休眠,所以当抢占或中断被禁用的时候,调用它就不再合法了。在一些情况下,可以通过递延操作要求spin_lock()等到抢占被重新启用的时候来解决这个问题。

当合法获取task_struct中的spinlock_t alloc_lock是,可以将put_task_struct()放到put_task_struct_delayed()队列中,以便延迟运行。

把mmdrop()放到mmdrop_delayed()队列中,延迟运行。

TIF_NEED_RESCHED_DELAYED重新调度,不过需要等到进程返回到用户空间,或者等到下一个preempt_check_resched_delayed()。无论哪种方式,关键点在于避免在唤醒高优先级任务直到当前任务未锁定之前无法取得进展的情况下进行不必要的抢占。没有TIF_NEED_RESCHED_DELAYED,高优先级任务会立刻抢占低优先级任务,只能被快速阻塞等待低优先级任务持有的锁。

解决方案是在spin_unlock()之后增加wake_up()去替代wake_up_process_sync()。如果唤醒的进程抢占当前进程,通过TIF_NEED_RESCHED_DELAYED,唤醒操作会被延迟。

在所有这些情况下,解决方案是将操作推迟到可以更安全或更方便地执行该操作。

降低延迟的操作

在PREEMPT_RT中的一些改变,主要目的是降低调度或中断延迟。

第一种改变是引入x86 MMX/SSE硬件。这个硬件在内核中处理中断禁用。某些情况下意味着等待直到MMX/SSE指令完成。一些MMX/SSE指令没有问题,但是有些指令要花很长时间,所以PREEMPT_RT拒绝使用这些很慢的指令。

第二个改变是使用per-CPU变量用于板坯分配器,以代替之前随意的中断禁用。

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

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

相关文章

Adb安装程序出现TimeOut错误

为什么80%的码农都做不了架构师?>>> 安装Apk过程中,出现如下错误: Failed to install on device ‘XXX′: timeout 原因时设备速度太卡,导致启动超时,解决办法:延长超时时间。 方法&#xff…

2021.NET大会日程首发!行程亮点全曝光!

{倒计时4天文末有福利→.NET机器人定制抱枕}2021年12月18日由中国各地技术社区共同发起举办、知名企业和开源组织联合协办的2021年中国.NET开发者大会即将盛大开幕▽2020/12/18-12/19主题:开源共建|开放创新|开发赋能形式:线上直播- 长按二维码免费领票 …

C++9

C++类和new、delete操作符 在C++中,你可以像定义变量一样来创建对象,如: Student stu; //对象已被实例化,已分配内存空间,可以使用了 stu.say(); //调用成员函数 这种情况下,系统会在栈区为对象分配内存。栈区是内存中的一块区域,由系统自动分配和释放,程序员无法操控…

一个技术人的知识管理方法论

http://www.cnblogs.com/me-sa/p/my_methodology.html转载于:https://www.cnblogs.com/liushiyong1/p/3556299.html

继Science发文后,Nature也发文评论曹雪涛“误用图片”调查结果

全世界只有3.14 % 的人关注了爆炸吧知识本文转自:募格学术2021年1月26日傍晚 Nature 网站以头条新闻的方式刊出了题为“著名中国免疫学家没有剽窃和学术造假”的新闻并配以曹雪涛的照片,该新闻大篇幅报道了科技部等多部门对于中国工程院院士曹雪涛的联合…

linux运行.pak文件,使用game-to-flatpak脚本将商业Linux游戏安装程序转换为Flatpak应用程序...

现在有一个新的脚本,它允许你将各种商业Linux游戏的安装程序转换成可以在各种GNU/Linux发行版上运行的Flatpak软件包。这是一个开源的shell脚本,由GNOME开发人员Bastien Nocera开发,它做了一件事,即自动将各种格式的商业Linux游戏…

Log4j 2漏洞(CVE-2021-44228)的快速响应

简介2021 年 12 月 9 日,在Log4j的 GitHub 上公开披露了一个影响多个版本的 Apache Log4j 2 实用程序的高严重性漏洞 CVE-2021-44228、CVSSv3 10.0 (https://logging.apache.org/log4j/2.x) 。该漏洞由阿里云安全团队的陈兆军(可能为音译)发现…

Xcode4.5编译ffmpeg成功,过程说明

最近项目要用音视频的通话,需要用ffmpeg来实现,但是ffmpeg在iOS平台上的编译有些问题。 1 下载代码 1.下载ffmpeg源代码 git clone git://source.ffmpeg.org/ffmpeg.git ffmpeg 参考:http://ffmpeg.org/download.html 2.下载ffmpeg-iphone…

何时使用[self release]

这样的语句 [self release];乍看上去让人很困惑。 从release方法本身的作用上来说,就是给self的引用技术减一,就像release对其他对象所做的一样。一般来说,唯一用到,也是最合适使用 [self release];这一写法的地方是在initXXX方法…

C++10

C++友元函数和友元类 一个类中可以有 public、protected、private 三种属性的成员,通过对象可以访问 public 成员,只有本类中的函数可以访问本类的 private 成员。现在,我们来补充介绍一个例外——友元(friend)。 fnend 的意思是朋友,或者说是好友,与好友的关系显然要比…

box2d 绘制圆

在让刚体听我的——ApplyForce、ApplyImpulse、SetLinearVelocity一节中,来自天地会的sxl001问道如何创建圆形的边界(Round Boundary),好吧,我用这个教程来回答他。 实际上Box2D中没有专门创建圆弧的API (b2CircleDef创建的是实体圆形不是圆弧…

linux浏览器不能播放音频文件夹,在html中插入音频文件在浏览器中播放音频文件的兼容性问题...

下面谈谈本人在html中插入音频文件,经过我的本地测试总结的一些问题(播放mp3文件):1、问题:IE8上正常(通过media player插件来播放)但在IE6和IE7上不会播放Firefox上要安装QuickTime插件才能播放Chrome通过将其转化成html5上的标签播放&#…

如何让 Timer 在特定时间点触发?

咨询区 Behrooz Karjoo我的应用程序需要做一个 事件触发 的功能,它需要每天定时执行,比如说当天的 16点,我现在的做法是使用一个 timer 按秒轮询判断当前是否为 16:00, 虽然可以玩得转,但我想能不能实现那种 16:00 自动…

深入剖析Android系统

深入剖析Android系统(基于Google发布的Jelly Bean原始代码,讲述Android系统的内部静态结构关系和内部运行机制,为你呈现原汁原味的Android代码分析大餐!) 杨长刚著 ISBN 978-7-121-19374-3 2013年1月出版 定价&#xf…

微博上的网红,为什么更能红得发紫?

“网红”是最近非常热潮的互联网现象。在微博上,各种各样的网红不仅活跃了粉丝群体(微博月活跃用户增至2.61亿),也为微博增添了互动热度(微博一季度微博日均视频播放量达4.7亿次,同比增长489%,比…

TotoiseSVN-小乌龟的使用方法总结

原文转自 http://www.cnblogs.com/xilentz/archive/2010/05/06/1728945.html 收藏起来用的时候比较方便 TotoiseSVN的基本使用方法在 项目管理实践教程一、工欲善其事,必先利其器【Basic Tools】中,我已经讲解了怎样安装TortoiseSVN。在上面的讲解中已经…

UNIX环境高级编程笔记

1.setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, len); SO_REUSEADDR套接口选项允许为以下四个不同的目的提供服务: 一.SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知的端口,即使以前建立的将该端口用作它们的本地端口的连接仍存在。 …

linux重定向输出时加时间变量,shell 重定向错误输出到文件 加上时间(标明错误抛出的时间)...

在开发中,我们避免不了写crontab脚本来异步执行一些东西,一般设置crontab用下面的方法:*/1 * * * * commond > /tmp/t.log 2>&1前面的> /tmp/t.log代表把标准输出重定向到/tmp/t.log文件,后面的 2>&1 代表把标…