μCOS 系列专题—实时系统及相关概念(1)
来源:互联网 发布:苹果手机主题软件 编辑:程序博客网 时间:2024/05/17 19:23
第一章节 实时系统及相关概念
1.实时系统(Real-Time System)
实时系统主要是指系统能否在规定的时间内完成相应的处理逻辑,并得到正确的结果。主要包括:硬实时系统和软实时系统。在软实时系统中,任务可以尽可能快地得到处理,但并不是在精确的时间内完成。在硬实时系统中,任务不仅能够正确完成,而且能够按时完成。大多数实时系统都是软实时和硬实时的综合折中方案。实时应用涵盖比较宽泛,一般地大多数嵌入式系统都是实时的,通过构建的实时系统,一台计算机在用户看来并不是一台。实时应用的设计比起非实时应用要复杂和困难一些。
2.前后台系统(Foreground/Background Systems)
前后台系统,即计算机前后台系统,早期的嵌入式系统中没有操作系统的概念,程序员编写嵌入式程序通常直接面对裸机及裸设备,在这种情况下,通常把嵌入式程序分成两部分,即前台程序和后台程序。
如下图所示,应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台行为。前台程序通过中断来处理事件;后台程序则掌管整个嵌入式系统软、硬件资源的分配、管理以及任务的调度,是一个系统管理调度程序。这就是通常所说的前后台系统。一般情况下,后台程序也叫事件处理任务或中断级任务,前台程序也叫事件处理级程序。在程序运行时,后台程序检查每个任务是否具备运行条件,通过一定的调度算法来完成相应的操作。对于实时性要求特别严格的操作通常由中断来完成,仅在中断服务程序中标记事件的发生,不再做任何工作就退出中断,经过后台程序的调度,转由前台程序完成事件的处理,这样就不会造成在中断服务程序中处理费时的事件而影响后续和其他中断。
实际上,前后台系统的实时性比预计的要差。这是因为前后台系统认为所有的任务具有相同的优先级别,即是平等的,而且任务的执行又是通过FIFO队列排队,因而对那些实时性要求高的任务不可能立刻得到处理。另外,由于后台程序是一个无限循环的结构,一旦在这个循环体中正在处理的任务崩溃,使得整个任务队列中的其他任务得不到机会被处理,从而造成整个系统的崩溃。由于这类系统结构简单,几乎不需要RAM/ROM的额外开销,因而在简单的嵌入式应用被广泛使用。
3.临界区代码(Critical Section of Code)
要谨慎对待临界区代码,一旦这部分代码执行之后,系统将不能被中断,否则就会崩溃。为确保系统安全,在执行临界区代码之前,一般采取的手段是关闭中断,执行完毕后再开启中断。
4.资源(Resources)
资源通常是任务使用的一个实体,资源可以是一个I/O设备如打印机、键盘或者显示器,资源也可以是一个变量、结构体或者数组。
5.共享资源(Shared Resource)
能够被一个以上任务共同使用的资源称为共享资源。为了防止数据访问冲突,每个任务必须获取独立的访问权限后才能对共享资源进行操作,而这种机制就称为互斥。
6.多任务(Multitasking)
多任务是CPU调度和切换处理机制。通过这种机制,单个CPU可以在多个任务之间切换执行,多任务好比拥有多个上下文环境的前后台系统。多任务机制最大化的提高了CPU的利用率,并且为多模块应用提供了可能。多任务最显著的一个特点就是允许程序员去管理复杂的实时应用。因此,采用多任务机制能够使程序开发的设计和维护变得更加容易。
7.任务(Task)
任务有时候也称为线程,任务可以看做是拥有CPU全部控制权的一个简单程序。实施应用的设计包括将一项工作分割成多个子任务来分别处理问题的某个部分。如下图所示,每个任务都被分配一个优先级、CPU寄存值集以及它的栈。一般地,每个任务都是在这5个状态之间的一个无限循环,同时只能处于一个状态。这5个状态分别:休眠(DORMANT)、就绪(READY)、 运行(RUNNING)、 等待(WAITING)和 中断(ISR)。这五个状态之间的迁移关系如下图所示:
休眠:处于此状态的任务仍旧驻留在内存中,但是已经不能成为多任务内核的一部分。
就绪:处于此状态的任务可以被执行,但是因为自身优先级低于当前正在运行任务的优先级。
运行:处于此状态的任务拥有着CPU的使用权。
等待:处于此状态的任务等待某个事件的发生,比如等待I/O操作的完成,等待共享资源的使用权,等待某个定时信号的产生,或者就是等待一段时间。
中断:处于此状态的任务表示,当某个中断已经发生,CPU正处于这个中断服务处理过程中。
8.上下文切换(Context Switch)或任务切换(Task Switch)
当多任务内核决定要进行任务切换时,需要将当前任务的上下文(CPU寄存器值)保存到它的上下文存储区域中——也就是任务的栈中。当这个操作执行完毕后,就会从新任务的栈中恢复其上下文环境,然后从上次中断的地方继续执行。这个处理过程就称为上下文切换或者任务切换。上下文切换增加了任务的额外管理的开销,CPU寄存器越多,开销就越高。上下文切换的执行时间取决于CPU有多少个寄存器需要保存和恢复。实时内核的性能评判,主要是取决于内核单位时间内执行上下文切换的能力。
9.内核(Kernel)
内核属于多任务系统的一部分,主要负责任务管理(比如CPU的使用时间)和任务间通信。内核提供的最基础服务就是上下文切换。一般采用实时内核可以简化系统的设计,通过将应用分割成多个任务,并交内核统一进行管理。
但是,由于内核提供的调度服务也需要时间,因此便增加了系统的额外开销。开销的多少取决于执行任务切换的频度。在一个设计好的应用中,一个内核大概会占用2%~5%的CPU运行时间。内核本身作为软件也属于应用中的一部分,他也会占用额外的ROM(或者代码空间)开销,以及用于存储额外数据结构和任务栈的RAM,并且有快速耗尽内存的趋势。
通常单片机因为RAM较少而难以运行一个实时内核,内核会提供必要的的服务如:管理信号量、邮箱、队列、延时等,这样就方便用户更好的使用处理器。我相信,一旦你使用过实时内核的话,保证你以后再也不想回到前后台系统的模式了。
10.调度器(Scheduler)
调度器作为内核的一部分,负责决定在接下来的任务切换中应该执行哪个任务。大多数实时内核都是基于优先级的调度。每个任务在创建时都会根据其重要程度被指定一个优先级,一般在应用中任务的优先级都是明确好的。在基于优先级的内核中,CPU的控制权一般会交给等待执行的高优先级任务。但是,具体地何时高优先级任务获得CPU 是由具体使用的内核来决定的,这里主要有两种:占先式内核和非占先式内核。(占先式就是在程序执行过程中,产生其他中断信号,这时内核转到终端服务程序,等终端服务程序执行完成后,再继续之前的程序。而非占先式在产生其他终端后,不去执行终端服务程序,而是继续执行当前程序,结束之后才去执行终端服务程序。)
11.非占先式内核(Non-Preemptive Kernel)
在非占先内核中,要求每个执行的任务能够交出CPU的控制权。为维护并发的假象,内核必须定期地对这些任务进行处理。非占先调度也称为合作型多任务,任务间通过相互协作来共享CPU。异步事件仍旧由中断系统服务来进行处理。在中断事件处理中,可以将就绪的高优先级任务切换到运行态,但还是要先返回到之前被中断的任务中,只有在当前任务交出CPU控制权之后,新的高优先级才可以获得CPU的控制权。
非占先内核的特点之一就是中断延迟较低(可以参考后面章节对中断的分析)。在任务级(或者从任务这个层面)来看,每个任务可以调用不可重入函数,而不必担心其它任务可能也在使用这个函数,从而造成数据破坏。因为每个任务在执行完毕后才会交出CPU控制权。当然,不可重入函数不能够被迫交出CPU控制权,也就说非占先内核中每个任务都是以主动的方式交出CPU的控制权。
使用非占先内核任务的响应速度比前后台系统要慢一些,这是因为任务的响应取决于占用时间最长的任务。
非占先内核的另一个特点就是,很少需要使用信号量去保护共享数据。每个任务都有自己的CPU,用户不需要担心某个任务被其他任务给抢占。但这并不总是绝对的,在有些实例中,还是会使用到信号量。共享I/O设备仍旧需要使用互斥信号量,比如一个任务在使用一台打印机的时候仍旧需要互斥(否则,在一张纸的第一行打印A文章,在第二行打B文章)。
非占先内核处理流程如下图所示:
(1)一个正在运行的任务被中断事件打断;
(2)如果使能中断,CPU通过中断向量表跳转到中断处理程序;
(3)中断处理程序处理事件,让就绪的高优先级任务切花到运行态;
(4)完成中断处理任务,执行中断返回指令,CPU继续执行被中断的任务。
(5)任务从中断位置处继续执行。
(6)任务执行完毕后,调用内核服务,释放CPU的控制权并交给另一个任务(也就是之前在中断服务例程中切换到就绪执行状态的任务)。
(7)内核看到高优先级任务已经切换到了准备执行状态(内核并不关心这个任务是否是在中断服务例程中被切换到这个状态,无关紧要),执行上下文切换,从而之前在中断服务例程中切换到就绪执行状态的任务便真正运行起来。
高优先级任务从就绪到执行必须等待较长的时间直到当前任务已经释放了CPU控制权。就像前后台系统中后台执行的程序,在非占先内核中任务的响应时间是不确定的,你从来都不知道最高优先级任务何时会获得CPU的控制权
概括的讲,非占先内核允许每个任务从运行直到自愿放弃CPU的控制权。中断会抢占任务,直到中断服务例程执行完毕,而后中断服务例程返回到之前被中断的任务,任务响应比前后太系统较好。商业内核中少有非占先式内核。
12.占先式内核(Preemptive Kernel)
在系统响应要求较高的场景中占先式内核应用较多。正因为如此,µC/OS-II和大多数商业实时内核一样都是占先式的。等待执行的最高优先级任务总是可以获得CPU的控制权。当某个任务将较高优先级任务切换到等待执行状态,并且当前任务能够被抢占(或者可以被挂起),那么高优先级任务将立即获得CPU的控制权。如果是在中断服务例程将高优先级任务切换到就绪态,那么等中断服务例程执行完毕后,之前被中断的任务就会被挂起,新切换为就绪态的高优先级任务将会被执行。
具体处理过程如下图所示:
(1)正在执行的任务被中断;
(2)如果使能中断,CPU通过中断向量表跳转到中断处理程序;
(3)中断处理程序处理事件,让就绪的高优先级任务切花到运行态,中断任务执行完毕后,内核提供的一个服务被唤醒(比如某个内核函数被调用);
(4)和(5)内核调用这个函数,得知一个更加重要的任务已经在等待执行,因此替代中断任务返回的是,内核将进行上下文切换,并执行更加重要的任务。当这个任务完成之后,内核调用另一个函数让这个任务进入休眠状态,等待下一个事件(如中断服务事件);
(6)和(7)内核得知要执行的下一个优先级较低,因此,进行上下文切换继续执行之前被中断的那个任务。
由于占先式内核执行最高优先级任务的时间是确定的,因此你可以确定它什么时候可以获得CPU的控制权。如此一来,占先式内核可以最小化任务的响应时间。
应用程序代码在使用占先式内核时不需要调用不可重入函数,除非当一个高优先级和一个低优先级任务使用一个公共的函数时,为了能确保够独立访问这些函数还是要使用互斥信号量。如果调用不可重入函数时,高优先级任务抢占了一个低优先级任务,不可重入函数的数据冲突可能会遭到破坏。综上所述,占先式内核总是先执行就绪的最高优先级任务,即使中断抢占了某个任务的CPU控制权,但在中断服务例程执行完毕后,内核会继续执行就绪的最高优先级任务(注意与非占先式不一样,此处不是去执行之前被中断的任务)。任务的响应时间得到了优化,并且是确定的,µC/OS-II就是一个占先式内核。
13.可重入(Reentrancy)
可重入函数是指可以被多个任务调用而不用担心数据破坏。可重入函数可以在任意时刻被打断,并且在随后的执行过程中不会出现数据丢失。可重入函数要么是使用局部变量,要么是使用全局受保护的变量。下面是一个可重入函数的例子:
因为函数strcpy()的参数副本保存在任务的栈中,因此当多个任务调用都函数strcpy()时,不用担心某个任务会破坏其它任务的数据指针。
举一个不可重入函数的例子,swap()函数是一个交换它的两个参数值的简单函数。为了下面讨论的需要,我假设你用的是一个抢占式内核,并切启用了中断,Temp是定义的一个全局变量,如下所示:
程序员想让swap()函数对任意一个函数都是可用的,下面我们来看一下,当一个正在执行swap()的低优先级任务被打断时会发生什么:
(1)当swap()函数被打断时Temp值为1;
(2)和(3)中断服务程序(ISR)将高优先级任务设置为就绪状态,因此在ISR执行完毕后,内核(假设为µC/OS-II)被唤醒并切换到这个任务。在高优先级任务中,变量Temp被设置为3且成功地完成了值的交换(在例中,z变成了4,t变成了3);
(4)调用内核服务延时服务OSTimeDly(1),高优先级任务将CPU控制权交给了低优先级任务,并等待下一个定时中断再被调用(后面会详细解释);
(5)随后,低优先级任务继续执行,注意此时Temp的值仍然是3。但是当低优先级任务执行完毕后,y的值从3被设置成了1。
注意此处只是举一个简单的例子,如何能让代码变得可重入也是比较明显的。你可以通过下列手段来让swap()函数成为可重入函数:
√将Temp声明为局部变量
√在执行这段代码之前关闭中断,执行完毕后开启中断
√使用信号量(后面会讲到)
√其他不容易解决的情形。在测试阶段不可重入函数产生的错误没有暴露出来,一旦产品发布时便很有可能发生。如果你对多任务的使用还是个新手,那么在使用到不可重入函数时候你就需要格外地小心。
如果中断是在swap()函数执行之前和之后发生的,那么对于例中的两个任务,x和y都能够正确地交换。
14.时间片轮番调度法(Round-Robin Scheduling)
当两个或两个以上任务拥有相同的优先级,内核允许一个任务执行预定的时间——时间额度,然后切换到另一个任务,这也叫做时间片调度法。在下列情形中,内核将交出CPU控制权:
√当前任务在分配的时间片内无事可做
√当前任务在分配的时间片内提前完成
√时间片用完
当前,µC/OS-II还不支持时间片轮番调度,在应用中每个任务都有一个唯一的优先级。
15.任务优先级(Task Priority)
为每个任务指定一个优先级,任务重要性程度越高,指派的优先级就越高。通常你要决定为每个任务指定相应的优先级。
16.静态优先级(Static Priority)
静态优先级是指在应用执行期间,任务的优先级不会发生改变。这样的话,在编译的时可为每个任务指定一个固定的优先级。在一个系统中那些优先级为静态地方,所有的任务和他们的定时阈值在编译期都是已知的。
17.动态优先级(Dynamic Priorities)
动态优先级是指在应用执行期间,任务的优先级可以发生改变。每个任务在运行期间可以改变它的优先级,拥有这个特性的实时内核应当避免出现优先级反转的情况。
18.优先级反转(Priority Inversions)
使用实时内核时,优先级反转情况是实时系统出现得最多的一个问题。下图解释了一个优先级反转的情况。Task1具有比Task2更高的优先级,Task2比Task3优先级高。
(1)任务Task1和Task2都在等待事件发生,Task3正在执行中;
(2)此时,任务Task3需要请求一个信号用于访问共享资源;
(3)为了能够获得资源,任务Task3执行了一些必要操作;
(4)任务ask1等待的时间发生,因此内核将任务Task3挂起,然后启动并执行任务Task1,因为此时Task1拥有较高的优先级;
(5)任务Task1执行;
(6)任务Task1执行了一会之后,也想访问某个共享资源(在例子中Task1试图获取Task3持有的信号)。因为Task3持有着这个资源,Task1就被加入到等待这个信号释放的任务队列中。
(7)Task3继续执行;
(8)在Task3执行了一段时间之后,便被Task2抢占了CPU的控制权,因为此时Task2等待的事件发生了。
(9)Task2继续运行;
(10)Task2处理完它所等待的事件,将CPU控制权又交回给了Task3。
(11)Task3继续运行;
(12)Task3使用资源完成工作之后,释放了这个资源的信号。此时,内核得知一个高优先级任务正等待此此信号,因此便进行上下文切换,继续执行Task1;
(13)此时,Task1持有此信号并且可以访问这个共享资源。
在上述的整个过程中,任务Task1的优先级仿佛降低到Task3之下,就是因为它需要等待Task3持有的资源。当Task2抢占了Task3时,这种让Task3被延迟的情况就更加恶化了。仅就在访问这个共享资源是,你可以通过提升Task3的优先级来及时纠正这种情况,当任务完成后再恢复为原始优先级。Task3的优先级应当提升到比其他共同竞争此资源的全部任务优先级还要高的一个等级,多任务内核应当允许任务优先级的动态修改以避免出现优先级反转的问题。然而,这样需要花费一些时间去修改任务的优先级。如果Task3在被Task1和Task2抢占之前已经完成了共享资源的访问会怎样?这时你在访问共享资源之前提升了Task3的优先级,然后在访问完毕后恢复到原始优先级,这样一来就浪费一定的CPU时间。真正需要防止优先级反转,内核需要能够自动改变任务的优先级。这就佳作优先级继承,µC/OS-II提供了这种特性。
(1)和(2)如前面例子中一样Task 3正在运行,但是此时因为访问一个共享变量而需要请求一个互斥信号;
(3)和(4)Task 3访问这个资源,然后被Task1抢占;
(5)和(6)Task 1执行并尝试去获取互斥信号。内核得知Task 3已经持有互斥信号,并且Task 3优先级比Task1要低一些,这种情况下,内核将Task3的优先级提升至与Task1一样的等级;
(7)内核将Task 1加入到互斥信号的队列中,然后继续执行Task3,这样一来Task3继续持有着这个共享资源;
(8)当Task3完成之后,释放互斥信号,此时内核将Task3的优先级恢复至原始值,然后查看此互斥信号等待队列中是否有等待此信号的任务。内核得知Task1正在等待,将此互斥信号交给它;
(9)现在,Task1就可以自由的访问这个共享资源了;
(10)和(11)当任务Task1执行完毕后,优先级处于中间的任务Task2获得CPU控制权。注意:在第(3)和第(10)步骤之间,本来任务Task2已经就绪等待执行,但这并不影响最终结果。这里仍旧有一些无法避免优先级反转的问题,但至少远已经降低了优先级反转造成的延迟情况的恶化程度。
- μCOS 系列专题—实时系统及相关概念(1)
- μCOS 系列专题—内核结构(1)
- μCOS 系列专题—前言
- μCOS 系列专题—互斥(Mutual Exception)
- μCOS 系列专题—分配优先级(Assigning Task Priorities)
- μCOS实时操作系统笔记
- 实时系统概念
- 实时系统概念
- 实时系统概念
- 实时操作系统相关概念整理
- Docker 快速上手系列(1): 镜像的概念及相关操作
- Docker快速上手系列(1):镜像的概念及相关操作
- PMU性能分析系列1 - 相关概念
- PMU性能分析系列1 - 相关概念
- 非客观书评(二)——《嵌入式实时操作系统μCOS-Ⅱ》
- 嵌入式实时操作系统的基本概念——μ/COS-II读书笔记
- 实时系统的一些概念
- SOA应用系统总体框架及相关概念
- mysql索引总结----mysql 索引类型以及创建
- myeclipse+maven搭建web项目方法二(超级详细)
- linux iscsi服务
- java中continue标记的使用
- keras中文文档笔记2——一些基本概念
- μCOS 系列专题—实时系统及相关概念(1)
- iOS APP跳转设置界面以及设置中的其他界面
- 如何在JSP页面中使用JSTL标签实现if和if-else判断,判断条件使用<%=value%>
- pyqt5入门—001—绘制ui界面
- redis学习笔记
- 鸟哥的Nginx私房菜 哈哈哈 收藏学习
- 罗素说理想与历程
- BZOJ 3309 DZY Loves Math
- textview实现图片切换效果