实时系统复习重点

网友投稿 637 2022-09-20

实时系统复习重点

实时系统复习重点

设计高可靠系统:

错误避免错误检测和移除容错

硬件

built in self test 自测试电路三模冗余:投票

软件

N版本程序:三模冗余的思想恢复模块

可重入与不可重入

函数可重入是指一个函数可以被多个任务调用, 而不需要担心在任务切换的过程中, 代码的执行会产生错误的结果

在μC/OS-II中, 可以同时有64个就绪任务, 每个任务都有各自的优先级。 优先级用无符号整数来表示, 从0到63,数字越大则优先级越低。 μC/OS总是调度就绪了的、 优先级最高的任务获得CPU的控制权, 不管这个任务是什么, 执行什么样的功能, 也不管该任务是否已经等了很久。

操作系统在启动的时候, 首先要在内存中创建一定是数量的任务控制块。

如果堆栈是向下增长, 也就是从高地址向低地址增长, 那么在任务刚开始创建后, 堆栈是空的。 相反, 如果堆栈是向上增长的, 栈顶在为TaskStk[0]。

设置任务就绪:

OSRdyGrp |= OSMapTbl[prio>>3];OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07];INT8U const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

获取最高优先级

y = OSUnMapTbl[OSRdyGrp];OSPrioHighRdy = (INT8U)(y<<3) + OSUnMapTbl[OSRdyTbl[y]]);INT8U const OSUnMapTbl[] = {...};

任务控制块初始化

OS_ENTER_CRITICAL(); OSTCBPrioTbl[prio] = ptcb; ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */ ptcb->OSTCBPrev = (OS_TCB *)0; if (OSTCBList != (OS_TCB *)0) { OSTCBList->OSTCBPrev = ptcb; } OSTCBList = ptcb; OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */ OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OSTaskCtr++; /* Increment the #tasks counter */ OS_EXIT_CRITICAL();

操作系统初始化函数OS_INIT是操作系统在开始运行的最初, 对全局变量、 任务控制块、 就绪表、 事件及消息队列等重要数据结构进行的初始化操作,并创建空闲任务、 统计任务等系统任务。 该函数必须在创建用户对象及调用OSStart()启动实时任务调度之前运行

删除任务是创建任务的逆过程, 任务创建设置就绪表, 就绪组, 任务删除则取消设置; 任务创建将任务控制块从空闲链表移到就绪链表; 删除操作则相反。

OSTaskSuspend将任务阻塞, 也就是被剥夺CPU的使用权而暂时终止运行, 转到阻塞状态。 通过OSTaskSuspend将任务转到阻塞态被称为挂起任务。被挂起的任务不能运行, 直到其他任务以该任务的优先级作为参数调用OSTaskResume来恢复它, 才能将该任务的状态重新设置为就绪状态。

OS_Sched中首先判断是不是具有任务调度的条件即:

无中断判断调度器没有上锁,满足条件后, 调用OS_SchedNew() 给全局变量OSPrioHighRdy赋值, 即给最高优先级任务赋值如果优先级最高的就绪任务不是当前在运行的任务, 调用OS_Task_SW() 进行任务切换

在OS_Task_SW中, 首先将CPU寄存器中的内容压入被换出的任务堆栈中, 然后将被换入的任务的堆栈内容放入到CPU寄存器, 在调度结束恢复时, 从控制器中取出上次保存的任务的地址, 完成了一次任务级调度。

空闲任务是μC/OS-II 的系统任务, 因为它占据了最低优先级63, 所以只有在其他的任务都因为等待事件的发生而被阻塞的时候才能得到运行

从空闲任务的代码可见, 空闲任务除了不停地将空闲计数器OSIdleCtr的值加1之外, 几乎什么都没有做。当没有任何其他任务能够运行的时候, 操作系统就会执行这段代码而OSTaskIdleHook默认的情况下也只是一个空函数, 没有特殊需要我们也不需要去填写它, 该函数的另一作用就是占据一点时间, 给系统足够的时间响应中断。

stat任务要除上100,原因: OSCPUUsage   = (INT8U)(100uL - OSIdleCtrRun / OSIdleCtrMax);

事件控制块ECB初始化

ECB初始化函数OS_InitEventList首先清空了所有的ECB块, 也就是清空了事件表。然后从0到OS_MAX_EVENTS - 1u)循环对除最后一个ECB块之外的所有ECB块进行初始化, 并顺便构建了单向的链表。循环结束后最后一个ECB 块OSEventTbl[OS_MAX_EVENTS - 1]进行初始化。 最后一个事件控制块OSEventTbl[OS_MAX_EVENTS - 1]的OSEventPtr域指向空地址0, 构造完成了如图4-3所示的空闲事件控制块 链表。然后将ECB空闲链表的表头地址给OSEventFreeList, 初始化完成。

设置事件等待

当任务等待事件发生, 并获得事件控制块ECB后, 需要在ECB中标记任务在等待事件的发生, 才可以在事件发生时取消任务的阻塞

标记。 在ECB中登记本任务, 即在ECB的事件等待表中对应优先级处标记为1, 事件等待组中对应位标记为1。

OSTCBCur->OSTCBEventPtr = pevent; /* Store ptr to ECB in TCB */ pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* Put task in waiting list */ pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;

取消标记。 在就绪表和就绪组中取消对该事件就绪的标记, 将就绪表中对应优先级处标记为0, 如果就绪表该任务所在的一组没有任务就绪, 将就绪组中的对应位标记为0。

y = ptcb->OSTCBY; pevent->OSEventTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX; /* 在事件等待表中删除事件等待标志 */ if (pevent->OSEventTbl[y] == 0u) { /*若该行已没有任务等待*/ pevent->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY; /*删除事件等待组的事件等待标志*/ }

将等待事件的任务就绪

任务因为等待事件而在ECB中登记自己的等待, 当事件发生的时候, 如果该任务是事件等待表中优先级最高的任务, 任务被取消等待而回到就绪状态。

在事件等待表和事件等待组中找到最高优先级的等待任务的优先级。根据优先级查优先级指针表, 找到该任务的任务控制块TCB指针。对任务控制块的相关参数进行赋值。判断任务是否被挂起, 如果未被挂起就将任务就绪。 完成从阻塞态到就绪态的转移。调用OS_EventTaskRemove在ECB的事件等待表中删除该任务。返回任务的优先级

提交信号量

当任务A获得信号量之后将信号量数字减1, 然后就可以访问资源R。 这时, 如果信号量的值为0, 任务B如果也要访问资源R, 必须等待信号量, 因此将任务B阻塞。 任务A在对资源的访问完成之后, 应将信号量唤醒。 因为资源已经可以被其他的任务访问了, 因此应该将任务B唤醒, 使任务B就绪。

当访问资源的任务有2个以上, 资源R可同时被N个任务访问, 因此信号量的值在最开始创建的时候应该等于N。 当任务A访问信号量, 信号量值变为N-1,任务B又访问, 信号量等于N-2, 当第M个任务访问, 信号量等于N-M。 当N-M=0的时候,也就是当N=M的时候, 当第N+1也要访问该资源R,第N+1个任务必须等待。 当任何一个任务(例如第2个)访问资源完成, 应该唤醒第N+1个任务让其访问资源。 当第N+1个任务访问完成之后, 因为没有其他的任务等待信号量, 只需简单地将信号量值加 1

放弃等待信号量

放弃等待信号量并非放弃本任务对信号量的等待。如果是放弃本任务对信号量的等待, 那么本任务在等待信号量, 那么本任务应该处于阻塞状态, 一个处于阻塞状态的任务得不到运行, 怎么能执行放弃等待信号量的代码呢? 因此, 一定是放弃其他任务对一个信号量的等待。

参数检查, 如果ECB指针无效或ECB的类型不是信号量类型, 返回参数检查错误信息。如果pevent->OSEventGrp为0说明没有任务等待信号量, 返回0。否则根据参数opt(选项)进行分支转移, 如果为OS_PEND_OPT_BROADCAST,使用while语句循环地将等待该信号量的每个任务用OS_EventTaskRdy来取消等待并使其就绪( 除非任务还被挂起) ; 如果为其他值则只将最高优先级的任务取消等待并就绪之。 两种情况下都返回取消等待信号量的任务数

EDF*

将先后次序改为时间约束,然后用EDF调度

先将后续任务的开始时间延后,再将前序任务的截止时间提前

RM

RM的上界Ulub : U_{lub}^{Rm} = n(2^{\frac{1}{n}} - 1)

双曲线上界 Hyperbolic Bound

\prod_{i=1}^{n}(U_i + 1) \le 2

响应时间分析

I_i = \sum_{D_k < D_i}C_k

响应时间Ri = Ci + Ii

验证Ri <= Di

干扰时间分析

I_ik = \lceil \frac{R_i}{T_k} \rceil C_k

I_i = \sum_{k=1}^{i-1} \lceil \frac{R_i}{T_k} \rceil C_k

EDF

因为EDF跟截止时间相关,是动态的,所以不能用响应时间分析。

处理器需求准则

\forall L > 0, \; g(0,L) \le L

g(0,L) = \sum_{i=1}^n \lfloor \frac{L-D_i+T_i}{T_i}\rfloor C_i

含义:gi(o,L) = 计算次数(时长/周期) * 计算时间,g(o,L) =

-Di+Ti为了保证整数运算,效果就是产生溢出,再向下取整

边界测试点

bounding test points

由于g函数只会在截止时间到来产生跳变,所以只要测试边界点就可以了如果任务同时到达,而且Up < 1,经过超周期后,任务都是重复出现的,最小的超周期为​​H = lcm(T1,...,Tn)​​G(0,L) = \sum_{i=1}^n(\frac{L+T_i-D_i}{T_i})C_i = LU + \sum_{i=1}^n(T_i - D_i)U_i

综合以上,得到处理器需求测试(Processor Demand Test)

\forall L \in D, \; g(0,L) \le L

D = {d_k | d_k \le \min(H,L^*)}

建立消息邮箱

检查是否这中断服务程序中创建消息邮箱。 同不允许在中断服务程序中创建信号量一样, 操作系统μC/OS-II同样不允许在中断服务程序中创建消息邮箱。检查是否有空闲的事件控制块。 将OSEventFreeList赋值给pevent, 如果pevent为空指针, 表示没有空闲的事件控制块, 函数返回。在事件控制块空闲链表中取下表头。 因为pevent现在已经是用于邮箱的事件控制块, 读者可以直接把他理解为一个邮箱。 那么, 需要执行的操作显然就是在事件控制块空闲链表中将他删除,这时候OSEventFreeList应该指向第二个ECB。对事件控制块赋值, 赋值后的ECB应该如图5-1所示。返回ECB地址。

当消息邮箱不再使用了, 就应该尽快归还给系统, 即将消息占用的ECB归还给ECB空闲链表以备它用。 消息邮箱的删除函数是OSMboxDel。 删除一个消息也要涉及方方面面, 因为可能有任务正在等待这个邮箱中的消息。

放弃等待邮箱也绝对不会是放弃本任务对邮箱的等待。放弃等待邮箱函数将放弃的是所有等待某邮箱的任务对该邮箱的等待或等待某邮箱的优先级最高的任务对邮箱的等待

检查事件控制块指针是否有效及事件控制块类型是否有效。如果pevent->OSEventGrp为0说明没有任务等待消息邮箱, 取消等待的任务数是0, 返回0。否则根据参数opt(选项)进行分支转移,如为OS_PEND_OPT_BROADCAST, 使用while语句循环地将等待该邮箱的每个任务用OS_EventTaskRdy来取消等待并使其就绪( 除非任务还被挂起) ;如果为其他值则只将最高优先级的任务取消等待并就绪之。返回取消等待信号量的任务数。

消息队列初始化函数在操作系统初始化时被调用, 主要用于初始化消息队列使用的数据结构。

将所有QCB全部清为全0。使用for循环将除最后一个消息控制块 OSQTbl[OS_MAX_QS - 1]之外的所有消息控制块初始化, 构建了单向的消息队列空闲链表。初始化最后一个QCB, 将消息队列空闲链表完善。 OSQFreeList指向链表的表头

创建消息队列就是将从ECB空闲链表中取下一个事件控制块ECB来, 将其用于消息队列管理。 并从QCB空闲链表的表头取下一个消息控制块QCB, 将其各种属性进行设置, 用于指示消息的位置以及提取和插入消息的位置。创建消息队列的函数名称为OSQCreate。

断是否在中断服务程序中调用本函数, 如果是就返回。取得消息队列的链表首地址送pevent。判断pevent是否为空指针, 如果是则说明是系统已经没有空闲的ECB可供使用,填写错误信息, 返回空指针。从空闲ECB链表取下表头。空闲QCB链表首地址送pq。如果没有有效的空闲QCB链表, 恢复空闲ECB链表, 返回空ECB指针。在空闲QCB链表中取一个pq指向的QCB,对其进行初始化。 设置OSQStart为消息指针数组的首地址start。OSQEnd值为&start[size]即消息指针数组( 消息队列) 中最后一个指针后面的一个地址。 OSQIn和OSQOut也设置为start。 OSQSize的值为size。 OSQEntries为0表示该队列中还没有消息。接下来对pevent指向的ECB进行初始化。 OSEventType为OS_EVENT_TYPE_Q表示用于消息队列管理。 OSEventCnt在这里没有用, 设置为0。 OSEventPtr指向QCB,即设置为pq。 调用OS_EventWaitListInit初始化ECB中的事件等待表和事件等待组。返回ECB指针。

当消息队列不再使用了, 就应该尽快归还给系统, 即将消息占用的ECB归还给ECB空闲链表以备它用, 将QCB也归还给空闲QCB链表。

创建内存分区

内存分区在操作系统初始化的时候并不存在。 在使用一个内存分区之前, 必须先定义一个二维数组, 但这个二维数组仍未成为内存分区。 通过调用函数OSMemCreate, 使用一个MCB对其进行管理, 才成为一个内存分区。 OSMemCreate返回一个指向内存控制块的指针, 供内存管理的其他操作函数调用。 首先, 当操作系统需要创建内存分区时, 调用OSMemCreate()函数, 并通过参数传递需要建立内存分区的属性, 包括: 内存分区的首地址, 内存分区中内存块的数量, 内存分区中内存块的大小, 以及返回信息代码的地址。 然后, 检查判断是否执行函数参数检查, 以保证内存分区的创建成功。 如果执行函数参数检查, 则判断: 内存分区地址是否有效, 内存分区地址大小是否一致, 内存分区是否至少含有两个内存块, 每个内存块是否起码可以容纳一个指针。 最后, 执行创建内存分区的算法, 创建一个内存分区, 并返回内存控制块的地址, 供系统调用。 内存分区创建的算法是: 将内存分区首地址加内存块大小得到下一个内存块地址, 然后再加内存块大小得到下一个内存块地址, 如此循环(内存块数量-1) 次, 直至最后一个内存块创建完成。 在其过程中, 通过指针建立内存块的链接表, 并将最后一个内存块内的指针指向空地址。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:python算法的工具选择
下一篇:Nginx 部署的虚拟主机如何使用 Let's Encrypt 来进行加密 https
相关文章

 发表评论

暂时没有评论,来抢沙发吧~