嵌入式系统设计要结课了,这课的氛围实属猛的一,学到了很多东西,非常感谢廖老师的教会我们来复习一波
国际班的朋友们,拿笔记复习的时候记得顺手点个赞!!!
嵌入式系统的四个主要讲点
- 任务管理(调度)
- 同步,信号量,交流(这部分讲的比较少)
- 内存管理 基本没怎么讲
- 中断 时间管理(对于事件的处理方法)
task任务
- 内核的功能是什么?调度让不同的程序分时共享CPU从而达到近似的并行
- 内核如何调度?给每一个执行程序分配一个task
- task是什么?一个可以执行的软件,有代码 数据 计算资源 操作系统当中举的例子是一个计算机科学家要做蛋糕,食谱是代码,剩下的准备材料和做的过程就是task, 从代码的角度来说,task是一个无限循环的void函数,他会在内存当中不断地被调度,比如QQ一直在后台运行,直到你强制退出。 task是一个逻辑上的存在,并且是动态存在的,根据冯诺依曼体系,我们可以看出代码是存 储执行的,即静态的,这也是task和code的主要区别 A task is an executable software entity specialized to perform a special action which includes sequential code and relative data and computer resource A task is an infinite loop function. it looks just like any other C function containing a return type and an argument but it must return. The return type of a task must always be declared to be void
- task的特点是什么?同步 异步 动态性(不是重点)
如何描述一个task?
- 上述定义的描述方法
- 代码描述
把内部的固定执行函数来看,task就是一个执行的函数,一个无限循环的void函数
- 为什么是无限循环? 因为一个任务要在系统当中不断地调度,而不是用一次就废了
- 为什么返回void,task是独立存在的,不需要为其他task做什么(即便需要也是共享或者通信)
下面我们通过代码进程块来了解一下task进程块在计算机当中的使用(调度)方法。
上面两段代码分别是两个task的代码。在循环外的代码主要目的是设置时钟节拍(晶体振荡器),剩下几个是寻找对应的引脚来控制指定引脚灯的开关(初始化)
A控制开灯,B控制关灯。
在μcOS操作系统当中维护着几个队列
- 就绪队列:等待执行的task,等待现在正在执行的task退出然后即可进入
- 延迟队列:延时的时间节拍不为0,暂时不能执行
为了防止CPU空转,在任务管理器当中设置一个空任务IDLE,简单讲就是占着茅坑不拉屎,等着别人去占用他。
有了这些基础知识,我们来看这两个任务如何工作的8
- 什么时候这些task被创建?操作系统当中有两种模式:核模式和运行模式(这个词我给搞忘了)只有在核模式下才可以创建任务。一般情况下,时间中断(也同样是个task,完成调度的任务)会打断运行进入核模式,创建任务
- 在哪被创建?在μcOS当中,为了保证时间复杂度的确定性,我们没有虚拟内存,都在内存当中被创建,放入就绪队列当中。
- 如何启动任务执行? 时间中断的task会在就绪队列当中选择优先级最高的task出队执行
在第一个时间片当中,A从就绪队列当中出队,执行,OSTimeDLy让他进入延迟队列当中,然后IDLE任务进入。
第一个时间片结束,时间中断任务进入,把所有延迟队列当中内容OSTimeDLy减1,选择就绪队列当中第二个任务执行。
二到九时间片都是IDLE空转,直到第10个时间片的时候,A的OSTimeDLy变为0,移入就绪队列当中再次执行。(我觉得讲清楚了)
所以灯是10个时间片一闪,再亮10个时间片的循环 一个时间片可以完成多个任务 上一个结束就会启动新的调度
task 和 process的区别是什么
我们还是通过上面的例子来说,上面两个例子两次的输出都是什么?
1 还是 2?
答案是task是1 process是2
task某种程度上可以理解为线程,他不像进程一样会产生阻塞。
task的控制核心->TCB描述任务
前面我们提到的task都是一个大的概念,这里我们通过细节的分析这个数据结构来掌握任务的管控情况。
除去额外的成员,TCB的组织基本如下(后面是占用字节数目的大小)
OS_STK *OSTCBStkPtr 4(堆栈指针) ptos
Os_tcb* OSTCBNext 4(双向链表的下一个)
Os_tcb* OSTCBPrev 4 (上一个)
Unsigned short OSTCBDly 2 (时延) 0
Unsigned char OSTCBStat 1 (状态) OS_STAT_RDY
Unsigned char OSTCBPrio 1 (优先级) (INT8U)prio
Unsigned char OSTCBX 1
Unsigned char OSTCBY 1
Unsigned char OSTCBBitX 1
Unsigned char OSTCBBitY 1
后面四个我们逐一来介绍
OSTCBNext 和 OSTCBPrev
这两个成员揭示了任务的存储方式即双向链表
为了方便,我们在boot初始化的时候就创建所有的TCB,64个(分别对应64个优先级)前两个是系统的优先级(用于处理紧急事件,比如掉电),后两个就是IDLE和系统监测(比如CPU温度)
所有的TCB都通过一个双向链表相连,称为TCBFreeList。如果TCB被某个对应优先级的task占用,就从Freelist到TCBList当中,代表正在使用的TCB。
显而易见,双向链表虽然能自适应的调整空间,但是查询时很慢的,所有系统当中还维护一个优先级队列(实质上是一个数组) 数组下标和优先级一一对应。
OSTCBStat
如果一个系统想要运行起来,就必须有一个完整的没有死状态的状态循环表,μcOS如下
这里可以看到我们之前提到的timetick(延迟队列到ready队列)还有吧啦吧啦一丢就不多说了,重点在于标识状态
OSTimeTick就是我们之前对应的时间中断task,每一次发出信号
OSTCBX OSTCBY OSTCBBitX OSTCBBitY
ptcb->OSTCBY=prio>>3 向右移动三位
ptcb->OSTCBBitY=OSMapTBI[ptcb->OSTCBY] 对应表当中的映射
ptcb->OSTCBX=prio & 0x07 对应低三位的与运算
ptcb->OSTCBBitX = OSMapTbpptcb->OSTCBX]
为了加速对最高优先级的查询过程,系统当中设计了一个数据结构标定任务是否被占用
这个方阵当中,如果被占用则为1,未占用则为0,(这里不是二维数组,而是一个数的不同位!!!)
PriorityReadyTable有8个数字,每个表示这一行当中的8个优先级是否有被占用
PriorityReadyGroup只有1个,8位,如果PriorityReadyTable的任意一项全部为0,则置0,否则为1.即检测该行是否有优先级被占用。
OSTCBX和OSTCBY分别对应着prio的高三位和低三位,也就分别对应着这个类似二维数组的两个索引。我们可以通过X和Y进行访问当前节点
OSTCBBitX OSTCBBitY的内容出现的目的是为了快速访问,也就是变成掩码的形式