lulu爱 发表于 2013-11-3 20:07:08

高效实时操作系统原理以及实践-实时操作系统原理

1 前后台系统的优缺点

前后台系统的架构用一句话来形容的话,就是一个大的循环,加上中断,就形成了一个整的体系。任务级别的称为后台, 中断是异步事件也称为前台。具体的流程图如下:



可以看到图里面处理了模块功能Task 4之后又返回处理模块功能Task 1,轮询不止。然后中断触发异步事件,一般也支持中断嵌套。

一般的单片机系统大多是采用了此种模式编程,优点是简洁,明了,新手的上手速度很快,特别是专注用来做一件事情的时候,一个while循环基本是无敌的。比如实现一个i2c slave ,完全可以用一个while 来模拟实现一个i2c 的slave 控制器。在操作系统上来模拟一个i2c slave 的话,基本不太可能实现,因为实时系统存在最大关中断的时间。



接下来谈谈前后台系统的缺点,前后台系统面临的一个直接困境是,软件规模大了,很难管理。处理的模块一多的话,实时性也根本难以保障。参照上图举例如下:
优先级高的事件一般在后台系统即任务空间去执行,而对于前后台系统而言一般优先级高的事件都是靠中断去解决,中断运行时间太长,后台任务响应速度很慢。
假设来了中断然后需要处理事件Task 4,如果Task 4的处理是最紧迫的,但是当前cpu 只运行到模块Task 1,这样的话,必须还要承受Task 2和Task3的运行时间延迟,才能轮到Task 4的处理,当模块越多的时候很可能情况更糟糕。这样的话可以看到紧急事件Task 4 的处理在时间上是不恒定的,有违实时系统原理。前后台的超循环设计对于紧急事件只能花大时间在中断内部去解决,但是当一个中断执行时间过长的时候会阻塞其它低优先级的中断的响应,继而造成其它中断数据的丢失。前后台系统还有一个缺点是各任务模块之间是相互独立的,缺乏有效的通讯手段。在一个中大型软件项目中,采用此类方法可以说是一种管理的灾难。

对于一个大while循环,另外一种实现方式是采用基于状态机的编程,往往能简化架构,解决复杂问题,比如目前的实时系统QP操作系统就是采取此种架构。但是状态机有一定的学习曲线,并不是每个人轻易能够掌握的。幸运的是raw os 除了支持传统的实时操作系统特性之外,也支持基于状态机的编程,具体细节请参考相应的章节。

综上所述,前后台的缺点是远大于优点的,所以就需要一个实时操作系统来解决这些缺点。

2 实时操作系统特性
实时操作系统存在的主要原因是用来解决前后台系统的不足,实时操作系统专注于管理任务,一个任务的形式通常是一个无限循环,任务和线程的概念是一致的,只是不同的操作系统叫法不同。实时系统目前在MCU,dsp上得到了广泛的应用。

实时操作系统主要是负责多个任务之间的管理通讯等,这样就形成了多任务的环境。多任务看上去像每个任务在同时运行,但是对于单核cpu来说,同一时刻只能跑一个任务。多任务环境相比前后台系统而言,最大化的利用了cpu效率,因为前后台系统存在空转的情况,但是多任务系统可以在系统空闲的时候跑空闲任务,甚至在空闲任务里实现低功耗的功能。

raw os 可以设置成抢占内核或者非抢占内核,当设置成抢占内核的时候,系统永远运行的是最高优先级的任务。如下图所示:


下面来解释下上图:
(1)        低优先级任务运行中,高优先级任务处于等待某一事件中。
(2)        中断发生了。
(3)        中断里面做了一些简单的事情后,通常是接收硬件数据,也就是完成中断上半部的事情后,就唤醒高优先任务,来进一步处理数据,比如网卡中断接收数据的处理流程。
(4)        中断结束后并不会回去运行低优先级的任务,而是调度运行高优先级。
(5)        高优先级任务运行中处理中断中接收的相关数据。
(6)        高优先级任务执行完毕,继续等待下一次事件,切换给低优先级任务继续运行。

一般一个实时系统需要占用5%左右的cpu运行时间,raw os 需要占用一定的rom和ram, 太低端的单片机比如8051是不推荐跑任何RTOS的。低端芯片主要ram比较紧缺,通过高度配置后,raw os 的ram占用量只有1K左右。一般通常情况是是4K以下的ram 占有量。低端的单片机强烈建议跑raw os的事件触发系统,只需要几十个字节的ram,完全可以符合开发需求。

3 如何安排实时操作系统任务优先级

很多实时操作系统书籍都没有明确的阐述如何去安排任务的优先级。任务优先级的安排一般都是架构师去安排,因为这个是一个全局性的战略,必须要做到对不同的任务轻重有别。

在安排任务优先级之前还有一个概念,那就是如何安排任务。

程序在CPU中是以任务的方式在运行,所以我们要将系统的处理框图转化为多任务流程图,对处理进行任务划分。任务划分存在这样一对矛盾:如果任务太多,必然增加系统任务切换的开销;如果任务太少,系统的并行度就降低了,实时性就比较差。在任务划分时要遵循H.Gomma原则:

   a. I/O原则:不同的外设执行不同任务;
   b. 优先级原则:不同优先级处理不同的任务;
   c. 大量运算:归为一个任务;
   d. 功能耦合:归为一个任务;
   e. 偶然耦合:归为一个任务;
   f. 频率组合:对于周期时间,不同任务处理不同的频率。

以上原则中,不同的外设执行不同任务这个原则尤其显现的重要。比如shell终端会用到串口,这个划分为一个单独的任务。又如网络协议栈会使用到网卡,这个时候协议栈内部会有专门的任务服务网卡。

安排任务的优先级犹如一个公司的老板,如何安排员工的职位。下面是安排任务优先级的一个可能的策略:
首先把系统里有哪些任务全部排列出来,这个是排优先级的基础。
以下是安排任务优先级考虑的两点因素:

1 以任务周期为依据,任务周期越短执行频率越高,优先级越高。

2 根据任务的重要程度,越重要的任务优先级越高。

以上两点有时需要一起综合考虑,因为任务周期越短的任务未必是重要的任务,必须结合第2点综合考虑。

4 实时操作系统主流调度方法

操作系统的调度方法有很多种,这里主要介绍主流的调度算法,像EDF这种调度算法就不表述了。目前几乎所有的商业实时操作系统的调度理论都基于RMS理论.
以下是基于RMS的一些假设理论:
1 各个任务之间没有资源共享,没有忙等,没有mutex, 也没有semaphore.
2 每个任务的最后期限是周期性的。
3 基于优先级抢占的,即高优先级任务一旦就绪的话,会立马抢占低优先级任务。
4 任务优先级的分配原则是,周期越短的任务,优先级越高。
5 任务切换以及纯内核任务的消耗忽略不计对于这个理论模型。

以下的公式是,基于RMS 的一个基础理论:


U 代表了系统的cpu 实际使用率,C 是任务的运行具体时间,T 是周期性的任务最后期限,n是有多少个任务,等式右边是理论上的可实时调度率,可以看到可调度率只和任务的个数有关,任务越多,可实时调度率越少。
可以看到等式右边:


可以看到当任务接近无穷大的时候,可调度率是69%了。从这个公式可以近似任务系统中69%的任务是可以硬实时调度的,即能在最后的期限中完成运行。还有大概31%的任务是非实时的。这样的理论模型在实际应用中是完全可行的。

接下来看一个实际的例子:

Process        Execution Time        Period
P1                         1                8
P2                         2                5
P3                         2                 10

假设有3个任务P1, P2, P3,运行的时间以及周期如上图。
根据公式可以算出系统的cpu 使用率。


理论上的可实时调度率为


可以看到0.725 <= 0.77976, 所以这3个任务都是可以硬实时调度的。

RMS理论主要是基于任务优先级的,对于指导分配分配任务的优先级有很大的实战价值,其核心是根据任务的最后期限(deadline)和周期的时间来分配任务优先级。周期越短的任务优先级越高,但是实战中周期越短的任务也不一定是实时性最高的任务,还是要根据应用程序去调整。不管如何这在优先级分配上是一个有趣的开始。

下图是基于raw os系统基于时间片的调度的一个实例:


RR全名叫Round Robin scheduling,是基于同等优先级的时间片调度。请参照上图, 上图中有3个任务,他们的优先级都为Y,这3个任务的调度方式都为 SCHED_RR,而且都赋予了一定的时间片,每一次定时器的中断都会把正在运行的任务时间减去1。参照上图举例如下:
(1)        任务3在运行中。
(3)        任务3时间片耗尽后,轮到任务1去运行。
(4)        任务1的时间片耗完之后又轮到任务2去运行。
最后又回到任务3去运行。

下图是一开始(1)时刻的任务优先级Y的就绪队列情况:


等到(4)时刻的时候任务优先级的就绪队列情况如下:

可以看到任务3的时间片用完之后,就跑到就绪队列最后去了。但是任务3还是在就绪队列里面。


以上原理基本是基于时间片调度的全部原理了。接下来谈谈基于FIFO 的调度策略。基于FIFO的调度策略也是posix规范所要求的,每一个任务都可以指定是基于时间片的SCHED_RR调度或者基于fifo的SCHED_FIFO调度。需要注意的是SCHED_RR或者SCHED_FIFO调度都是基于任务的同等优先级的调度,不同优先级的任务之间不存在此种形式的调度。

假如有一个优先级Y的就绪队列如下:


可以看到上图中任务1的调度方式为SCHED_FIFO, 只要任务1不放弃cpu的使用权的话,任务2和任务3是永远不能运行的,当任务1阻塞在内核对象上了,比如semaphore, queue, event 等会轮到任务2去运行,或者任务1调用raw_sleep函数也可以轮到任务2去运行。


5 关于raw os 的外围模块
raw os主要专注于设计世界上最前沿的实时操作系统内核,此外还能支持运行一系列著名的开源软件,比如网络协议栈uip以及lwip, fat文件系统fat32,nand文件系统yaffs2等等。目前的支持的GUI为ucgui, 不过ucgui是商业收费的,具体有用户去决定是否使用。值得一提的是这些外围模块全部都能在VC环境下模拟仿真,给用户整个项目的全模拟带来可能性。

6 raw os的系统特性

raw os 提供了标准C代码。内核源码的发布是一个干净的版本,不带任何移植。raw os的标准C代码已经在数十个芯片平台移植并验证。通过此书,用户将学到实时操作原理以及如何有效的使用好raw os。

raw os的api函数具有高度的直观性。用户基本看到raw os的api名字以及参数基本就知道该怎么使用api了。

raw os是可以配置为多任务的抢占的。raw os可以配置为多任务的抢占型内核,系统中永远运行的是最高优先级的任务,最大限度的保障了系统的实时性。

raw os支持系统配置成非抢占系统。非抢占的系统的好处是可以追求系统数据的最大吞吐量,还能提高系统对驱动的容错能力,因为很多时候驱动的错误是驱动设计人员没有完全考虑到系统的抢占的因素引起的,当驱动人员无法完全掌控抢占系统的时候,关掉系统抢占可能是一个很好地选择。

raw os的调度策略主要是基于RMS理论,即基于任务优先级的。任务的优先级数值越小,表明任务的优先级越高,rms 调度策略在无穷任务情况下能保证至少70%的任务是可以完全实时的,即70%的任务都能在最后的期限完成工作。目前的商业用的实时系统都是基于rms理论的。

raw os支持同等优先级任务,主要基于时间片轮调度。raw os允许多个任务跑在同等优先级,当多个同等优先级的任务处于就绪队列中时,raw os会基于时间片调度任务,也就是说每个任务都有一个时间片,时间片消耗完了就插入到就绪队列后面去,轮到下一个任务跑。这个时间片是任务创建的时候由用户来指定,也可以有用户动态去改变。处于运行中的任务也可以放弃自己的时间片,排队到就绪队列的最后去。

超低的系统最大关中断时间。raw os通过把内核临界区关抢占,而不是关中断的实现方式,并结合一些特殊的技术,成功的把raw os整个系统的最大关中断时间降低到0us, 此技术可以保证响应一些非常快速的中断源。

独具特色的中断下半部。raw os的中断下半部支持task 0 以及workqueue,为实时性更进一步加一层保障。

丰富的内存分配算法。raw os 支持block, byte, slab, page, malloc等内存分配算法。

支持内存保护。raw os在具有MPU的cpu上,诸如cortex-m3, cottex-m4上支持MPU内存保护,此项技术可以使用于医疗等高度安全领域。

状态机级别的协程支持。支持idle任务级别的事件驱动(协程),基于状态机机制(fsm+hsm), 所有idle 级别的事件任务共享一个栈,此项技术可以大大节约ram, 适用于资源缺乏却又想使用RTOS的场合。

raw os的所有api在时间上都是可确定性的。时间的可确定性的意思是说,时间是恒定的,实时操作系统不在于快,而在于时间的恒定,稳定性,不会有时间不恒定性的api存在。

raw os的高度可配置性。raw os的所有模块都是可配置性的,在资源上可以多可以少,最小的内核可配置成占用2K以下的代码,几十个字节的ram。可以适应多种项目的应用需求。

raw os的高度可移植性。raw os已经在很多的cpu平台上移植,比如arm 7, arm 9,cortex-m3,cortex-m4,mips32,等等cpu上。未来会有更多的cpu平台支持。


raw os 官网地址为:www.raw-os.org
页: [1]
查看完整版本: 高效实时操作系统原理以及实践-实时操作系统原理