实时操作系统(Real-timeoperatingsystem,RTOS),又称即时操作系统,它会根据排序运行、管理系统资源,并为开发应用程序提供一致的基础。实时操作系统与通常的操作系统相比,最大的特色就是“实时性”,假如有一个任务须要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。这些特点保证了各个任务的及时执行。
实时操作系统(Real-timeOS)是相对于分时操作系统(Time-SharingOS)的一个概念。在一个分时操作系统中,计算机资源会被平均地分配给系统内所有的工作。在分时系统中,各项任务须要花多长时间来完成,这一点并不重要。
所以在一个实时操作系统之中,最关注的是每位任务在多长时间内可以完成。简单地说,实时和分时操作系统最大的不同在于期限(deadline)”这个概念。在一个特定任务的执行时间内必须是确定的而且可预测的,在任何情况下都能保证任务的最大执行时间限制,一般实时分为软实时和硬实时。
软实时:仅仅要求风波的响应是实时的,并不要求任务必须在多长的时间内完成,大多数情况下要求的时统计意义上的实时,而不须要100%达到实时。在许多情况下,这些软性正确性已然达到了用户期望的水平。例如用户在操作DVD播放的时侯,时常不能在限定的时间内完成任务也是可以接受的,它可以容忍碰巧的超时错误,失败导致的后果并不严重
硬实时:在任务的执行时间的要求是十分严格的,无论在哪些情况下,任务的执行时间必需要得到绝对的保证,否则将会形成灾难性的后果。例如linux 删除文件夹,车辆碰撞后,必须在X时间内弹开安全气帘,你弹开晚了,人早已挂了。
1为何linux不是硬实时
Linux系统一开始就被根据GPOS(通用操作系统)来设计的,它所追求的是尽量减短系统的平均响应时间,提升吞吐量,达到更好的平均性能。在这个背景下,Linux难以达到强实时性的诱因是多方面的,例如虚拟显存管理、共享资源互斥访问机制等等,但最重要的诱因是进程调度以及内核占据机制,这也是本文讨论的重点。
我们首先来瞧瞧,为何Linux不是一个硬实时的操作系统,其主要有以下几个诱因
spinlock是一个随处可见被内核和驱动使用的API
首先,我们来瞧瞧spinlock的实现,spin_lock()会调用preempt_disable()造成本核的占领调度被关掉(preempt_disable函数实际降低preempt_count来达到此疗效),其次我们理解spin_lock_irq()是local_irq_disable()+preempt_disable()的合体。
对于两个插口,你们是不是很熟悉,我们在linux内核和驱动程序中随处可见,在不用睡眠,时间较短的临界区的场景,我们就会第一时间想到spinlock。载流子锁的优点是,在两个人(这两个人可能是线程与线程,中断与线程linux time服务,中断与中断等)同时竞争一个锁的时侯,避免出现失败的那一方出现上下文切换,所以希望在原点等待。
【文章福利】小编在群文件上传了一些个人认为比较好得学习书籍、视频资料,有须要的可以进群【977878001】领取!!!额外附赠一份价值699的内核资料包(含视频教程、电子书、实战项目及代码)
内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料
学习直通车(腾讯课堂免费报考):Linux内核源码/显存调优/文件系统/进程管理/设备驱动/网路合同栈-学习视频教程-腾讯课堂
并且这样的载流子锁本身也会造成一些副作用,它造成了持有该锁的CPU核的占领被严禁,所以内核载流子锁的实现,是通过严禁占领来实现临界区的保护,如右图的事例
假定T1,T2,T3,T4运行在一个核里面,当T1领到spinlock后,这个核上的占领调度被严禁
Linux的中断执行时间可能过长且不可嵌套
当中断发生后,通常硬件会手动屏蔽当前CPU对于中断的响应,而软件层面上,直至IRQHANDLER做完,才能重新开启中断,例如,对于ARM处理器而言,exception进来的时侯,硬件就会手动屏蔽中断
也就是说,当ARM处理器收到中断的时侯,它步入中断模式,同时ARM处理器的CPSR寄存器的IRQ位会被硬件设置为屏蔽IRQ。
Linux内核会在如下2个时侯重新开启CPSR对IRQ的响应:
从IRQHANDLER返回中断底半部的SOFTIRQ从IRQHANDLER返回一个线程上下文
中断在执行的时侯,所有的中断都进不来,这个设计本身简化了内核,并且对于硬实时的严打是致命的,上面的中断不执行完成,优先级再高的中断也得给我等着。
例如中断1在执行的过程中,来了中断2,而中断2对应的事情是必需要决定性码流的,因为IRQ1的中断服务程序也是码农写的,我们难以确定这个中断服务程序要执行多久。这似乎让高优先级中断2的步入延后不再具备可预期性。
软中断(softirq)是一个比进程上下文优先级更高的上下文
我们构想一个场景,哪怕Linux解决了问题2,就是Linux的中断变地可嵌套,高优先级的中断可以打断低优先级的中断,但是高优先级的中断2唤起了一个用户写的实时线程。
IRQ2唤起了实时任务T1,然而T1必须等待IRQ1激起的软中断(也包括使用软中断上下文的tasklet等)被执行完,T1就能投入执行。IRQ1激起的softirq的代码是码农写的,这个码农写多久,鬼都不晓得,这似乎破坏了实时任务T1得以调度执行的确定性串扰。
内核上面会屏蔽中断的API如local_irq_disable、spin_lock_irqsave等
这么,问题又来了,spin_lock_irqsave既屏蔽了占据,又屏蔽了中断,这会造成中断和实时任务的确定性信噪比导致不可预期的破坏。由于spin_lock_irqsave和spin_lock_irqrestore是码农写的,鬼都不晓得它要多久。
所有针对这个问题,我们回顾linux中的四类区间:
上述四类区间中,只有第四类区间支持占领调度。当可以调度的事情发生在前3类区间中,即若果在这3类区间中唤起了高优先级的可以占据的tasklinux time服务,实际上却不能占领,直至这3类区间结束。
用一个事例说明如下:
如上图所示:
从T1到T6,这个区间的时间是不可预测的,因而通用的Linux系统难以达到硬实时的标准。
2linux内核实时性改进
早在2001年时,内核就开始打上占据补丁,回顾之前的知识点
主要是用thread_info数据结构中的一个preempt_count计数
preempt_count包含preempt,softirq,hardirq,nim以及need_resched几个域
内核提供preempt_disable来关掉占领,然后preempt_count会加1,preempt_enable函数用于打开占据,preempt_count计数会减1,程序会判定当前是否为0,倘若为0,就调用___preempt_schedule
preempt_count本质上是一个per-CPU的32位变量,它在各类处理器构架下的储存位置和命名不尽相同,但其值都可以使用preempt_count()函数统一获取。preempt_count逻辑相关的核心代码坐落include/linux/preempt.h,即使只是一个32位变量,但因为其和中断、调度/占据密切相关,因而在系统中发挥的作用不容轻视。
对于内核仅仅的支持占领调度,要达到硬实时系统的要求还远远的达不到要求,因此社区仍然旨在于linux内核实时性优化和改进工作linux 输入法,近来几年有好多优化的补丁和技巧,
PREEMPT_RT补丁可以通过以下方面对kernel进行源码级的整修:
因而将Linux内核中的1/2/3类区间都改导致4类区间,大大增强了系统的实时性。对于linux也提供了好多工具用于检测什么地方有比较大的调度延时