Linux的实时任务调度

通用Linux系统支持实时和非实时两种进程,实时进程相对于普通进程具有绝对的优先级。对应地,实时进程采用SCHED_FIFO或者SCHED_RR调度策略,普通的进程采用SCHED_OTHER调度策略。

在调度算法的实现上,Linux中的每个任务有四个与调度相关的参数,它们是rt_priority、policy、priority(nice)、counter。调度程序根据这四个参数进行进程调度。

在 SCHED_OTHER调度策略中,调度器总是选择那个priority+counter值最大的进程来调度执行。从逻辑上分析SCHED_OTHER 调度策略存在着调度周期(epoch),在每一个调度周期中,一个进程的priority和counter值的大小影响了当前时刻应该调度哪一个进程来执行,其中priority是一个固定不变的值,在进程创建时就已经确定,它代表了该进程的优先级,也代表这该进程在每一个调度周期中能够得到的时间片的多少;counter是一个动态变化的值,它反映了一个进程在当前的调度周期中还剩下的时间片。在每一个调度周期的开始,priority的值被赋给 counter,然后每次该进程被调度执行时,counter值都减少。当counter值为零时,该进程用完自己在本调度周期中的时间片,不再参与本调度周期的进程调度。当所有进程的时间片都用完时,一个调度周期结束,然后周而复始。另外可以看出Linux系统中的调度周期不是静态的,它是一个动态变化的量,比如处于可运行状态的进程的多少和它们priority值都可以影响一个epoch的长短。值得注意的一点是,在2.4以上的内核中,priority被nice所取代,但二者作用类似。

可见SCHED_OTHER调度策略本质上是一种比例共享的调度策略,它的这种设计方法能够保证进程调度时的公平性--一个低优先级的进程在每一个epoch中也会得到自己应得的那些CPU执行时间,另外它也提供了不同进程的优先级区分,具有高priority值的进程能够获得更多的执行时间。

对于实时进程来说,它们使用的是基于实时优先级rt_priority的优先级调度策略,但根据不同的调度策略,同一实时优先级的进程之间的调度方法有所不同:

SCHED_FIFO:不同的进程根据静态优先级进行排队,然后在同一优先级的队列中,谁先准备好运行就先调度谁,并且正在运行的进程不会被终止直到以下情况发生:

1.被有更高优先级的进程所强占CPU

2.自己因为资源请求而阻塞

3.自己主动放弃CPU(调用sched_yield)

SCHED_RR:这种调度策略跟上面的SCHED_FIFO一模一样,除了它给每个进程分配一个时间片,时间片到了正在执行的进程就放弃执行。时间片的长度可以通过sched_rr_get_interval调用得到。

但是由于Linux系统本身是一个面向桌面的系统,所以将它应用于实时应用中时存在如下的一些问题:

  • 当一个进程调用系统调用进入内核态运行时,它是不可被抢占的 
  • Linux内核实现中使用了大量的封中断操作会造成中断的丢失
  • Linux系统中的调度单位为10ms,所以它不能够提供精确的定时
  • 由于使用虚拟内存技术,当发生页出错时,需要从硬盘中读取交换数据,但硬盘读写由于存储位置的随机性会导致随机的读写时间,这在某些情况下会影响一些实时任务的截止期限
  • 虽然Linux进程调度也支持实时优先级,但缺乏有效的实时任务的调度机制和调度算法。它的网络子系统的协议处理和其它设备的中断处理都没有与它对应的进程的调度关联起来,并且它们自身也没有明确的调度机制

Linux内核2.6中加入的实时功能

在 2.6 内核中,通过对内核进行简单配置使其完全可抢占,您就可以得到软实时功能。

正常来讲,当用户空间的进程执行内核调用时(通过系统调用),它便不能被抢占。这意味着如果低优先级进程进行了系统调用后,高优先级进程必须等到调用结束后才能访问 CPU。新的配置选项 CONFIG_PREEMPT 改变了这一内核行为,在高优先级任务可用的情况下(即使此进程正在进行系统调用),它允许进程被抢占。

但这种配置选项也是一种折衷。虽然此选项实现了软实时性能并且即使在负载条件下也可使操作系统顺利地运行,但这样做也付出了代价。代价就是略微减低了吞吐量以及内核性能,原因是 CONFIG_PREEMPT 选项增加了开销。

在 2.6 内核中另一项有用的配置选项是高精度定时器。这个新选项允许定时器以 1μs 的精度运行(如果底层硬件支持的话),并通过红黑树实现对定时器的高效管理。通过红黑树,可以使用大量的定时器而不会对定时器子系统(O(log n))的性能造成影响。

只需要一点额外的工作,就可以通过 PREEMPT_RT 补丁实现硬实时。PREEMPT_RT 补丁提供了多项修改,可实现硬实时支持。其中一些修改包括重新实现一些内核锁定原语,从而实现完全可抢占,实现内核互斥的优先级继承,并把中断处理程序转换为内核线程以实现线程可抢占。

参考资料:https://blog.csdn.net/u013178472/article/details/81115080

猜你喜欢

转载自blog.csdn.net/FYZDMMCpp/article/details/90524688