实时操作系统概念

定义全局变量的方法:
  #ifdef OS_GLOBALS  //当在.c文件中定义该宏时则把.h文件中的变量定义为全局变量,否则只声明为外部变量。
  #define OS_EXT
  #else
  #define OS_EXT extern
  #endif
  OS_EXT INT32U OSIdleCtr;
  OS_EXT INT32U OSIdleCtrRun;
  OS_EXT INT32U OSIdleCtrMax;
  
  Eg:
  #define OS_GLOBALS
  #include “includes.h”  
  //此时定义头文件中的全局变量


OS_ENTER_CRITICAL():关中断
OS_EXIT_CRITICAL():开中断
  开中断和关中断是为了保护临界的代码,关中断会影响中断延迟。用户还可以用信号量来保护临界代码段。


任务的5种状态:休眠、就绪、运行、挂起、中断


内核: 多任务系统中,内核负责管理各个任务,或者说为每个任务分配 CPU 时间,并且负责任务之间的通讯。内核提供的基本服务是任务切换。 内核本身对 CPU 的占用时间一般在 2 到 5 个百分点之间。










不可剥夺型内核:
  不可剥夺型内核要求每个任务自我放弃 CPU 的所有权。不可剥夺型调度法也称作合作型多任务,各个任务彼此合作共享一个 CPU。异步事件还是由中断服务来处理。中断服务可以使一个高优先级的任务由挂起状态变为就绪状态。但中断服务以后控制权还是回到原来被中断了的那个任务,直到该任务主动放弃 CPU 的使用权时,那个高优先级的任务才能获得 CPU的使用权。
  不可剥夺型内核的一个优点是响应中断快。在讨论中断响应时会进一步涉及这个问题。在任务级,不可剥夺型内核允许使用不可重入函数。函数的可重入性以后会讨论。每个任务都可以调用非可重入性函数,而不必担心其它任务可能正在使用该函数,从而造成数据的破坏。因为每个任务要运行到完成时才释放 CPU 的控制权。当然该不可重入型函数本身不得有放弃 CPU 控制权的企图。
  使用不可剥夺型内核时,任务级响应时间比前后台系统快得多。此时的任务级响应时间取决于最长的任务执行时间。不可剥夺型内核的另一个优点是,几乎不需要使用信号量保护共享数据。运行着的任务占有 CPU,而不必担心被别的任务抢占。但这也不是绝对的,在某种情况下,信号量还是用得着的。处理共享 I/O 设备时仍需要使用互斥型信号量。例如,在打印机的使用上,仍需要满足互斥条件。


不可剥夺型内核优缺点:
  不可剥夺型内核的一个优点是响应中断快;缺点是与前后系统一样,不可剥夺型内核的任务级响应时间是不确定的,不知道什么时候最高优先级的任务才能拿到CPU 的控制权,完全取决于应用程序什么时候释放 CPU。


可剥夺型内核:
  当系统响应时间很重要时,要使用可剥夺型内核。因此,μC/OS-Ⅱ以及绝大多数商业上销售的实时内核都是可剥夺型内核。最高优先级的任务一旦就绪,总能得到 CPU 的控制权。当一个运行着的任务使一个比它优先级高的任务进入了就绪态,当前任务的 CPU 使用权就被剥夺了,或者说被挂起了,那个高优先级的任务立刻得到了 CPU 的控制权。如果是中断服务子程序使一个高优先级的任务进入就绪态,中断完成时,中断了的任务被挂起,优先级高的那个任务开始运行。
  使用可剥夺型内核时,应用程序不应直接使用不可重入型函数。调用不可重入型函数时,要满足互斥条件,这一点可以用互斥型信号量来实现。


可剥夺型内核优点:
  最高优先级的任务什么时候可以执行,可以得到 CPU 的控制权是可知的。使用可剥夺型内核使得任务级响应时间得以最优化。


可重入性:
  可重入型函数可以被一个以上的任务调用,而不必担心数据的破坏。可重入型函数任何时候都可以被中断,一段时间以后又可以运行,而相应数据不会丢失。可重入型函数或者只使用局部变量,即变量保存在 CPU 寄存器中或堆栈中。如果使用全局变量,则要对全局变量予以保护。


时间片轮番调度法:
  当两个或两个以上任务有同样优先级,内核允许一个任务运行事先确定的一段时间,叫做时间额度(quantum) ,然后切换给另一个任务。也叫做时间片调度。内核在满足以下条件时,把CPU控制权交给下一个任务就绪态的任务:
1、当前任务已无事可做
2、当前任务在时间片还没结束时已经完成了。
uC/OS-II不支持时间片轮转调度法。


静态优先级:
  应用程序执行过程中诸任务优先级不变,则称之为静态优先级。在静态优先级系统中,诸任务以及它们的时间约束在程序编译时是已知的。


动态优先级:
  应用程序执行过程中,任务的优先级是可变的,则称之为动态优先级。实时内核应当避免出现优先级反转问题。

优先级反转:
  使用实时内核,优先级反转问题是实时系统中出现得最多的问题。任务 1 优先级高于任务 2,任务 2 优先级高于任务 3。任务 1 和任务 2 处于挂起状态,等待某一事件的发生,任务 3 正在运行。此时,任务 3要使用其共享资源。使用共享资源之前,首先必须得到该资源的信号量(Semaphore)。任务 3 得到了该信号量,并开始使用该共享资源。由于任务1 优先级高,它等待的事件到来之后剥夺了任务 3 的 CPU 使用权,任务 1 开始运行。运行过程中任务 1 也要使用那个任务 3 正在使用着的资源,由于该资源的信号量还被任务 3 占用着,任务 1 只能进入挂起状态,等待任务 3 释放该信号量。任务 3 得以继续运行。由于任务 2 的优先级高于任务 3,当任务 2 等待的事件发生后,任务 2 剥夺了任务 3 的 CPU 的使用权并开始运行。处理它该处理的事件,直到处理完之后将 CPU 控制权还给任 3。任务 3 接着运行,直到释放那个共享资源的信号量。直到此时,由于实时内核知道有个高优先级的任务在等待这个信号量,内核做任务切换, 使任务 1 得到该信号量并接着运行。
  在这种情况下,任务 1 优先级实际上降到了任务 3 的优先级水平。因为任务 1 要等,直等到任务 3 释放占有的那个共享资源。由于任务 2 剥夺任务 3 的 CPU 使用权,使任务 1 的状况更加恶化,任务 2 使任务 1 增加了额外的延迟时间。任务 1 和任务 2 的优先级发生了反转。
  纠正的方法可以是,在任务 3 使用共享资源时,提升任务 3 的优先级。任务完成时予以恢复。任务 3 的优先级必须升至最高,高于允许使用该资源的任何任务。多任务内核应允许动态改变任务的优先级以避免发生优先级反转现象。然而改变任务的优先级是很花时间的。如果任务 3 并没有先被任务 1 剥夺 CPU 使用权,又被任务 2 抢走了 CPU 使用权,花很多时间在共享资源使用前提升任务 3 的优先级,然后又在资源使用后花时间恢复任务 3 的优先级,则无形中浪费了很多 CPU 时间。真正需要的是,为防止发生优先级反转,内核能自动变换任务的优先级,这叫做优先级继承(Priority inheritance)但μC/OS-Ⅱ不支持优先级继承,一些商业内核有优先级继承功能。


优先级继承:
  如果内核支持优先级继承的话,在上述例子中会是怎样一个过程。首先任务 3 在运行,任务 3 申请信号量以获得共享资源使用权,任务 3 得到并开始使用共享资源。后来 CPU 使用权被任务 1 剥夺,任务 1 开始运行,任务 1 申请共享资源信号量。此时,内核知道该信号量被任务 3 占用了,而任务 3 的优先级比任务 1 低,内核于是将任务 3 的优先级升至与任务 1 一样,然而回到任务 3 继续运行,使用该共享资源,直到任务 3 释放共享资源信号量。这时,内核恢复任务 3 本来的优先级并把信号量交给任务 1,任务 1 得以顺利运行,任务 1 完成以后那些任务优先级在任务 1 与任务 3 之间的任务例如任务 2 才能得到 CPU 使用权,并开始运行。


信号量(Semaphores):
  信号量实际上是一种约定机制,在多任务内核中普遍使用。信号量常用于:
控制共享资源的使用权(满足互斥条件)
标志某时间发生。
使两个任务的行为发生同步。


  信号像是一把钥匙,任务要运行下去,得先拿到这把钥匙。如果信号已被别的任务占用,该任务只得被挂起,直到信号被当前使用者释放。换句话说,申请信号的任务是在说:“把钥匙给我,如果谁正在用着,我只好等!”信号是只有两个值的变量,信号量是计数式的。只取两个值的信号是只有两个值 0 和 1 的量,因此也称之为信号量。计数式信号量的值可以是 0 到 255 或 0 到 65535,或 0 到 4294967295,取决于信号量规约机制使用的是 8 位、16位还是 32 位。到底是几位,实际上是取决于用的哪种内核。根据信号量的值,内核跟踪那些等待信号量的任务。
  一般地说,对信号量只能实施三种操作: 初始化(INITIALIZE), 也可称作建立(CREATE);等信号(WAIT)也可称作挂起(PEND);给信号(SIGNAL)或发信号(POST)。信号量初始化时要给信号量赋初值,等待信号量的任务表(Waiting list)应清为空。
  想要得到信号量的任务执行等待(WAIT)操作。如果该信号量有效(即信号量值大于 0),则信号量值减 1,任务得以继续运行。如果信号量的值为 0,等待信号量的任务就被列入等待信号量任务表。多数内核允许用户定义等待超时,如果等待时间超过了某一设定值时,该信号量还是无效,则等待信号量的任务进入就绪态准备运行,并返回出错代码(指出发生了等待超时错误)。

死锁(Deadlock):
  死锁也称作抱死,指两个任务无限期地互相等待对方控制着的资源。设任务 T1 正独享资源 R1,任务 T2 在独享资源 R2,而此时 T1 又要独享 R2,T2 也要独享 R1,于是哪个任务都没法继续执行了,发生了死锁。最简单的防止发生死锁的方法是让每个任务都:
  1、先得到全部需要的资源再做下一步的工作
  2、用同样的顺序去申请多个资源
  3、释放资源时使用相反的顺序
  内核大多允许用户在申请信号量时定义等待超时,以此化解死锁。当等待时间超过了某一确定值,信号量还是无效状态,就会返回某种形式的出现超时错误的代码,这个出错代码告知该任务,不是得到了资源使用权,而是系统错误。死锁一般发生在大型多任务系统中,在嵌入式系统中不易出现。


同步:
  Task1()
  {
  for (;;) {
  Perform operation;
  Signal task #2;
  Wait for signal from task #2;
  Continue operation;
  }
  }
  Task2()
  {
  for (;;) {
  Perform operation;
  Signal task #1;
  Wait for signal from task #1;
  Continue operation;
  }
  }

中断响应: 
  中断响应定义为从中断发生到开始执行用户的中断服务子程序代码来处理这个中断的时间。中断响应时间包括开始处理这个中断前的全部开销。典型地,执行用户代码之前要保护现场,将 CPU 的各寄存器推入堆栈。这段时间将被记作中断响应时间。


前后台系统和不可剥夺型内核:中断响应时间 = 中断延迟 + 保存 CPU 内部寄存器的时间


可剥夺型内核: 中断响应 = 中断延迟 + 保存 CPU 内部寄存器的时间 + 内核的进入中断服务函数的执行时间(OSIntEnter())


中断响应是系统在最坏的响应情况下给出的时间。


中断恢复时间(Interrupt Recovery):
  中断恢复时间定义为微处理器返回到被中断了的程序代码所需要的时间。


前后台系统和不可剥夺型内核:中断恢复时间 =  恢复 CPU 内部寄存器值的时间 + 执行中断返回指令的时间


可剥夺型内核: 中断恢复时间 = 判定是否有优先级更高的任务进入了就绪态的时间 + 恢复那个优先级更高任务的 CPU 内部寄存器的时间 + 执行中断返回指令的时间

你可能感兴趣的:(UCOS)