RTOS基本原理介绍.doc
- 文档编号:14662139
- 上传时间:2023-06-25
- 格式:DOC
- 页数:26
- 大小:343KB
RTOS基本原理介绍.doc
《RTOS基本原理介绍.doc》由会员分享,可在线阅读,更多相关《RTOS基本原理介绍.doc(26页珍藏版)》请在冰点文库上搜索。
RTOS基本原理介绍
目录
第一节RTOS概述
1-1
1.1什么是RTOS
1-1
1.2一个简单的实时多任务设计
1-2
第二节任务
1-5
2.1什么是任务
1-5
2.2任务的结构
1-5
2.3任务的调度
1-6
2.4异常处理
1-10
2.5VxWorks中提供的系统调用
1-10
第三节同步和互斥
1-11
3.1互斥问题
1-11
3.2同步问题
1-12
3.3关中断解决同步、互斥问题
1-13
3.4信号量
1-13
3.5VxWorks提供的系统调用
1-18
第四节进程间通信
1-19
4.1使用共享内存区,来实现数据的传递
1-19
4.2消息(Message)
1-19
4.3消息队列的应用
1-20
4.4管道(pipe)
1-21
4.5VxWorks提供的系统调用
1-21
第五节总结
1-22
5.1通信原语
1-22
5.2死锁问题
1-22
5.3推荐阅读资料
1-22
i
RTOS基本原理
第一节RTOS概述
P目标:
掌握RTOS的基本概念和设计要求。
通过举例说明RTOS的好处。
1.1什么是RTOS
1、什么是实时
实时系统是指能在确定的时间内执行其功能,并对外部的异步事件作出响应的计算机系统。
确定的响应时间是其最根本的要求。
实时操作系统是一个实时系统最关键的组成部分。
实时多任务环境允许一个实时应用作为一系列独立任务来运行,各任务有各自的线程和系统资源。
延时(latency)是指发生的事件和事件的响应之间的延时。
实时系统的另一关键特性是硬件中断处理。
2、实时系统的要求
控制多种外部元素的能力,包括:
独立的元素;
异步的元素;
同步的元素;
高速的执行,包括:
快的响应;
低的开销;
确定的操作:
延迟的响应是错误的响应;
3、实时系统的基本组成(以VxWorks为例)
高效的实时内核:
主要包括基于优先级的任务调度、任务同步和通信、中断处理、定时器和内存管理。
I/O系统:
包括网络,管道,RAM,SCSI,键盘,显示器,磁盘,并口等。
文件系统。
网络组件。
虚拟内存等。
?
想一想:
为什么Win9x不是RTOS?
1.2一个简单的实时多任务设计
上图所示为一个机器手,现在来编程对它进行控制。
如果使用单线程来设计,算法如下:
arm()
{
while
(1)
{
if(shoulderneedsmoving)
moveShould();
if(elbowneedsmoving)
moveElbow();
if(wristneedsmoving)
moveWrist();
......
}
}
这种设计的缺点:
1、难以实现不同部位按照不同的速度移动
2、难以设置哪个部位可以优先动作
3、不能实现任务的抢占。
优点:
设计简单,没有任务切换的开销。
如果使用多任务设计,算法如下:
对于每个机器臂都开一个任务:
joint()
{
for(;;)
{
waitForMove();
moveJoint();
}
}
每个线程在“ready”和“waiting”之间不停转化。
等待可以是延时一定时间,或者是其他的一个事件,比如“动动手腕”的命令。
多任务的优点:
每个任务可以使用不同的等待命令,控制机器臂按不同的速度运动。
如果由两个任务同时处于“ready”态,可以使用优先级调度算法让最重要的任务先运行。
即如果两个手指都要动,可以选择最重要的手指先动。
在使用抢占式调度时,可以使重要的任务一旦进入“ready”,就可以立即运行(打断先前正在运行的不是很重要的任务)。
可以方便的添加更多的元素(每个机器臂使用不同的控制方式,有的是延时,有的是等待下发命令等)。
第二节 任务
P目标:
掌握RTOS(以VxWorks为例)中任务的定义、状态和调度算法。
2.1什么是任务
任务是代码运行的一个映象,从系统的角度看,任务是竞争系统资源的最小运行单元。
任务可以使用或等待CPU、I/O设备及内存空间等系统资源,并独立于其它任务,与它们一起并发运行。
VxWorks内核使任务能快速共享系统的绝大部分资源,同时有独立的上下文来控制个别任务的执行。
注意:
所谓的并发执行只是宏观上的现象,而实际上在任一时刻,CPU只能执行一个任务的指令。
微观上并发执行只能发生在多CPU系统上。
&技术细节:
进程:
线程:
2.2任务的结构
多任务系统中,正在执行着的任务能随时被打断,对内部和外部发生的事件在确定的时间里作出响应。
VxWorks实时内核Wind提供了基本的多任务环境。
从表面上来看,多个任务正在同时执行,实际上,系统内核根据某一调度策略让它们交替运行。
系统调度器使用任务控制块的数据结构(简记为TCB)来管理任务调度。
任务控制块用来描述一个任务,每一任务都与一个TCB关联。
TCB包括了任务的当前状态、优先级、要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。
调度器在任务最初被激活时以及从休眠态重新被激活时,要用到这些信息。
此外,TCB还被用来存放任务的"上下文"(context)。
任务的上下文就是当一个执行中的任务被停止时,所要保存的所有信息。
在任务被重新执行时,必须要恢复上下文。
通常,上下文就是计算机当前的状态,也即各个寄存器的内容。
如同在发生中断所要保存的内容一样。
当发生任务切换时,当前运行的任务的上下文被存入TCB,将要被执行的任务的上下文从它的TCB中取出,放入各个寄存器中。
于是转而执行这个任务,执行的起点是前次它在运行时被中止的位置。
&技术细节:
VxWorks中,内存地址空间不是任务上下文的一部分。
所有的代码运行在同一地址空间。
如每一任务需各自的内存空间,需可选产品VxVMI的支持。
2.3任务的调度
1、任务的状态
实时系统的一个任务可有多种状态,其中最基本的状态有四种:
就绪态(ready):
任务只等待系统分配CPU资源,正在运行的任务也属于就绪态;
悬置态(pend):
任务需等待某些不可利用的资源而被阻塞(如等待共享资源被释放或者等待某个事件发生);
休眠态(suspend):
如果系统不需要某一个任务工作,则这个任务处于休眠状态;
延迟态(delay):
任务等待一定的时间再进入就绪态;
2、状态迁移
3、任务的调度策略
多任务调度须采用一种调度算法来分配CPU给就绪态任务。
Wind内核采用基于优先级的抢占式调度法作为它的缺省策略,同时它也提供了轮转调度法。
基于优先级的抢占式任务调度具有很多的优点。
这种调度方法为每个任务指定不同的优先级。
处于就绪态的最高优先级任务将一直运行下去,当更高优先级的任务由就绪态进入运行时,系统内核立即保存当前任务的上下文,切换到更高优先级的任务。
&技术细节:
Wind内核划分优先级为256级(0~255)。
优先级0为最高优先级,优先级255为最低。
当任务被创建时,系统根据给定值分配任务优先级。
然而,优先级也可以是动态的,它们能在系统运行时被用户使用系统调用taskPrioritySet()来加以改变,但不能在运行时被操作系统所改变。
Wind内核也提供了轮转调度法(RoundRobin)。
轮转调度法分配给处于就绪态的每个同优先级的任务一个相同的执行时间片。
时间片的长度可由系统调用KernelTimeSlice()通过输入参数值来指定。
很明显,每个任务都有一个运行时间计数器,任务运行时每过一个时间滴答就加1。
一个任务用完时间片之后,wind内核就进行任务切换,停止执行当前运行的任务,将它放入就绪队列尾部,将运行时间计数器置零,并开始执行就绪队列中的下一个任务。
当运行任务被更高优先级的任务抢占时,此任务的运行时间计数器被保存,直到该任务下次运行时。
&技术细节:
可通过调用taskLock()和taskUnlock()来使调度器起作用和失效。
当一个任务调用taskLock()使调度器失效,任务运行时没有基于优先级的抢占发生。
然而,如果任务被阻塞或是悬置时,调度器则从就绪队列中取出最高优先级的任务运行。
当设置抢占禁止的任务解除阻塞,再次开始运行时,抢占又被禁止。
这种抢占禁止防止任务的切换,但对中断处理不起作用。
如果要屏蔽/打开中断,可以使用intLock()/intUnlock()。
4、任务调度的时机
系统调用发生时;
中断发生时(最典型的就是系统时钟中断引发任务调度);
2.4异常处理
程序代码和数据的出错,如非法命令、总线或地址错误、被零除等。
VxWorks异常处理包,一般是将引起异常的任务休眠,保存任务在异常出错处的状态值,内核和其它任务继续执行。
用户可借助Tornado开发工具,查看当前任务状态,从而确定被休眠的任务。
2.5VxWorks中提供的系统调用
参见VxWorks帮助中的taskLib
第三节同步和互斥
P目标:
掌握临界区的概念。
掌握RTOS中同步、互斥问题的解决方案。
掌握信号量的使用方法。
了解优先级翻转的概念。
3.1互斥问题
1、竞争条件
考虑一个简单的例子。
有两个任务,它们将不定时的打印一些东西,如下:
TaskA TaskB
{ {
TaskAWaitForActive(); TaskBWaitForActive();
printf("TaskAprint\n"); printf("TaskBprint\n");
} }
假设某次TaskA打印到“TaskA”时,系统时钟中断到来,认为该TaskB执行了,于是TaskB打印出“TaskBprint”,结果屏幕上就是
“TaskATaskBprint\nprint\n”
类似这样的情况,即两个或多个进程使用某些共享资源,而最后的结果取决于进程运行的精确时序,就称为竞争条件(raceconditions)。
有竞争条件的程序运行时,有可能发生一些无法解释的问题。
2、临界区
实际上所有牵涉到共享内存,共享文件,以及共享任何资源的情况都会引发与前边类似的错误。
要避免这种错误,关键是要找到某种途径来阻止多于一个的进程同时读写共享的数据。
换言之,我们需要的是互斥(mutualexclusion),即以某种手段确保当一个进程在使用一个共享资源时,其他进程不能作同样的操作。
前述问题的问题就在于,在进程A对共享资源的使用未结束之前进程B就使用它。
为实现互斥而选择合适的原语是任何操作系统的主要设计内容之一。
避免竞争条件的问题也可以用一种抽象的方式进行描述。
一个进程的一部分时间做内部计算或其他一些不会引起竞争条件的操作,在其他时候进程可能会访问共享内存或者共享文件,或执行其他一些会导致竞争的操作。
我们把对共享内存(包括所有的I/O操作)进行访问的程序片段称作临界区或者临界段(criticalregion或者cirticalsection)。
如果我们能够安排使得两个进程不可能同时处于临界区,则就能够避免竞争条件。
为了保证使用共享数据的并发过程能够正确和高效的进行操作,一个好的解决方案需要以下4个条件:
任何两个进程不能同时处于临界区;
不应对CPU的速度和数目作任何假设;(在单CPU系统上不用考虑)
临界区外的进程不得阻塞其他进程;
不得使进程在临界区外无休止地等待。
历史上对于互斥的问题有过很多种解决方法,具体可以参见《现代操作系统》第二章,会发现互斥问题解决的发展历程还是比较曲折的。
3.2同步问题
在多任务系统中,由于各任务之间是异步执行,而各任务之间有可能存在着一种先后逻辑问题,这是就需要一种同步机制来保证进程的按顺序执行。
任务需要等待某个事件发生
忙等待(eg:
不停的轮询)效率很低
在事件发生以前挂起(pend)更好
3.3关中断解决同步、互斥问题
最简单的方法是使每个进程在进入临界区后先关闭中断,在离开之前再打开中断。
屏蔽中断等于也屏蔽了任务调度,故此时对临界区的操作是很安全的(在VxWorks中,任务调度由可能在系统调用后重新发生,故此时还必须锁住任务)。
该方案最大的优点是简单,快速,但是把关中断的权力交给用户进程是不明智的。
设想一下如果一个进程关掉中断后,由于后面分支众多,导致某个分支里面忘了开中断,结果如何?
系统可能就会崩溃。
关中断还有一个问题,由于系统要求通过定时中断来进行任务调度,如果关中断时间过长,将导致系统的实时性下降,这是不可接受的。
然而对于操作系统内核来说,关闭中断是一项有用的技术,因为它可以保证开关中断的配套以及保证关中断时间不至于过长。
由上可见,对于共享资源进行保护的最重要一点就是对它的操作是一个原子操作(Atomicoperation),即操作为不可打断的。
注意:
对于我们的单板软件程序来说,关中断还是比较有用的,因为我们比较强调速度,而且公司RTOS规范也是推荐使用的。
不过大家使用时一定要十分小心,保证开关中断的配套,以及关中断时间一定要很短。
3.4信号量
1、信号量简介
信号量其实就是一个全局的整型数,不过它由系统管理,对它的操作必须通过系统调用来进行。
基本操作有两种:
加操作,减操作,分别称为Up,down操作,也有叫(P,V)操作的。
VxWorks中称之为Give和Take操作。
对信号量的操作不会引起竞争条件,最重要的一点就是对它的操作是不可打断的。
一般操作系统都是通过关中断来保证不会引起竞争条件。
关中断期间,只进行以下操作:
检测信号量,修改信号量,如果需要,使调用进程挂起。
由于这些操作只需要几条机器指令,因此关中断不会带来什么副作用。
(对于多CPU,有其他的解决方案)
2、用二进制信号量解决同步问题(VxWorks中)
建立一个二进制信号量,以表示事件的状态(BinarySemaphore)
二进制信号量只有两种状态:
FULL(事件已经发生)
EMPTY(事件未发生)
等待事件的任务调用系统函数semTake(),它将被挂起,直到此信号量被释放(即表示事件已经发生)。
任务或者中断服务程序检测到事件后,调用semGive,将引起任务调度,使得等待任务变为ready态。
看一下semTake和semGive的流程。
比如说我们的邮箱程序:
它需要等待主控写完邮箱后,才能进行操作,于是它使用了一个信号量,用于表示这个事件。
VoidmbInit()
{
SemBCreate(SEM_Q_FIFO,SEM_EMPTY);
TaskSpawn(taskMBRecvAndSend,....);
IntConnect(MB_INT,intMbRecv();
}
VoidtaskMBRecvAndSend()
{
FOREVER
{
SemTake(semMailBox,WAIT_FOREVER);//等待主控写完邮箱
MbProcessRecv();
MbProcessSend();
}
}
VoidintMbRecv(void)
{
SemGive(semMailBox); //通知taskMBRecvAndSend主机已经交权
}
这样,邮箱接收发送进程就实现了和主机写邮箱的同步问题。
?
想一想:
为什么不在中断中直接处理主机发送过来的命令
B诀窍:
当多个任务等待同一个事件的时候,也可以使用二进制信号量。
当信号量被give时,根据初始化的设置,可以激活等待队列中第一个任务或者等待队列中优先级最高的任务。
如果需要一个事件同时激活多个任务,VxWorks还提供了一个系统调用,semFlush,它将激活所有take此信号量的任务。
3、用二进制信号量解决互斥问题
使用二进制信号量也可以解决部分互斥问题,如下例所示:
SemMux=semBCreate(SEM_Q_PRIORITY,SEM_FULL);
//注意,此时需要初始化为SEM_FULL
VoidtaskA(void)
{
SemTake(semMux);
临界区1;
SemGive(semMux);
}
VoidtaskB(void)
{
SemTake(semMux);
临界区2;
semGive(semMux);
}
?
想一想:
此时二进制信号量是如何进行互斥保护的。
使用二进制信号量进行互斥,会产生一些问题,其中一个问题就是优先级翻转问题(PriorityInversion)。
4、优先级翻转
所谓优先级翻转问题是指高优先级的任务由于等待被低优先级的任务占用的共享资源,而被中优先级的任务抢占执行的问题,如下图所示:
对于优先级翻转问题,现在是一个比较热门的话题,各种操作系统有的提供了解决方案,有的没有提供,VxWorks提供了一种解决方案,它是通过对二进制信号量进行了一些改进得来,叫互斥信号量(Mutual-ExclusionSemaphores)。
semId=semMCreate(SEM_Q_PRIORITY|SEM_INVERSION_SAFE);
它使用了称之为优先级继承的方法来解决翻转问题,它保证了进入临界区的任务的优先级为所有在这个共享资源上等待的任务的最高优先级,在退出临界区后,它的优先级又自动降为原先的优先级,请看下图:
此外,互斥型信号量还有一些其他的功能,如防止任务被删除,增加了owner的概念等,具体可以参见《VxWorksProgrammer'sGuide》的basicOS一章。
5、互斥信号量
互斥信号量专门用于解决多个进程之间共享资源问题,它对互斥、优先级翻转、进程安全删除、递归等专门提供了优化处理。
它的规则是:
只用于互斥;
只能被take它的任务give;
不能用在ISR中;
不能使用semFlush释放;
6、用信号量解决事件计数器问题
实际问题中,还有一类,比如说“来一个人,倒一杯水”问题,它存在着一个计数问题,即进程需要知道来了多少个人(因为可能一下来了两个)。
VxWorks提供了一种称之为计数器信号量(CountingSemaphore),它的特点是,每次进行semGive()操作后,信号量的值就加一,每次进行semTake后,信号量的值就减一,如果已经为0,则挂起。
请看解决方案:
SemWaterCup=semCCreate(SEM_QUEUE_FIFO,0);//初始不需要到水。
VoidtaskA(void)
{
....;
SemGive(SemWaterCup); //来了一个人
}
VoidtaskB(void)
{
....;
SemTake(semWaterCup,WAIT_FOREVER); //进入临界区
GiveOneCupOfWater();
}
这样就实现了来客人和到水之间的同步。
3.5VxWorks提供的系统调用
参见VxWorks帮助手册中的semLib,semBLib,semCLib,semMLib
第四节进程间通信
P目标:
掌握消息队列的使用方法
&技术细节:
术语:
进程间通信-InterProcessCommunication(IPC)
实际上,信号量就是一种简单的进程间通信机制,但是如果进程间需要大量的数据进行通信,使用信号量就不是太方便了。
4.1使用共享内存区,来实现数据的传递
此种方案一般需要使用信号量等机制实现同步互斥等,比较麻烦。
注意:
使用全局变量就是一种典型的共享内存,设计时一定要注意进行互斥保护。
4.2消息(Message)
VxWorks提供了一种称之为消息队列的机制(MessageQueue)。
它是一个FIFO,并内置了同步和互斥手段。
创建一个消息:
MSG_Q_IDmsgQCreate(maxMsgs,maxMsgLength,options)
消息队列的操作比较简单,只需要预先定义好消息队列的大小,使用时调用msgQSend(),如果队列满了,则任务将被挂起,取数据时相反,如果队列为空,则挂起。
4.3消息队列的应用
1、数据的采集和分析
此时一般会有一个高级别的任务或者中断进行实时的数据采集,然后把数据发送到消息队列中,由另外一个低级别任务慢慢进行计算。
2、client-sever结构
4.4管道(pipe)
还有一种称之为管道的机制,此处不作介绍,有兴趣的可以参见《VxWorksProgrammersGuide》的basicOS一章。
4.5VxWorks提供的系统调用
参见VxWorks帮助中的msgQLib
第五节总结
P目标:
知道通信原语的概念
知道死锁问题的概念
5.1通信原语
在多任务系统中,由于任务的执行是随机的,为了保证正确的对数据和事件进行响应,必须使用一些同步和互斥手段。
这些手段称之为通信原语,进程之间通过这些原语进行通信,他们确保了对于临界区的访问在同一时刻只能有一个进程进行。
原语有很多种,且新方法总是层出不穷。
目前最流行的四种原语有:
信号量,事件计数器,消息,管程。
实质上,他们都是等价的,借助其中任何一种方法,都能够到处其他三种原语。
具体可以参见《现代操作系统》一书。
?
想一想:
原语本身至少有两个进程访问,为何不会出现互斥问题?
5.2可重入问题
在我们的软件中,经常存在着一个函数被多个任务、中断调用的情况,如printf函数,如果在这些函数里面使用全局变量或者静态变量,就会出现问题。
printf就是一个典型的有可重入问题的函数。
?
想一想:
会出现什么问题,为什么会出问题?
怎么解决可重入问题?
5.3死锁问题
不适当的使用原语,有可能会引起一个严重的问题,死锁。
有兴趣可以参考《现代操作系统》一书。
5.3推荐阅读资料
《VxWorks系统及Tornado开发环境培训》之《Real-Time_Multitasking》
《现代操作系统》AndrewS,Tanenbaum
《VxWorksProgrammer'sGuide》之BasicOS
\\BDSOFTSVR\资料\操作系统\Tornado
1-26
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- RTOS 基本原理 介绍