目录
- μC/OS-Ⅱ任务调度
- 1.任务控制块
- 2.任务管理
- 3.任务状态
- μC/OS-Ⅱ时间管理
- μC/OS-Ⅱ内存管理
- 内存控制块MCB
- μC/OS-Ⅱ任务通信
- 1.事件
- 2.事件控制块ECB
- 3.信号量
- 4.邮箱
- 5.消息队列
操作系统内核:在多任务系统中,提供任务调度与切换、中断服务
操作系统内核为每个任务分配CPU时间,并且负责任务之间的通信。
嵌入式操作系统核心内容:
1、进程调度
2、进程通信
3、内存管理
4、设备管理
嵌入式操作系统的结构:
μC/OS-Ⅱ任务调度
用户在建立操作系统的时候需要初始化操作系统。
任务调度的时候需要开始操作系统。
中断的代码不允许其他中断,需要进入代码临界段函数,临界段完成后使用退出代码临界段函数。
有些任务间一些处理过程比较重要,不希望被别的任务打断,需要使用任务调度上锁函数,完成之后需要使用任务调度解锁函数。
1.任务控制块
任务控制块是一个基于链表的数据结构,任务控制块主要用于记录任务的堆栈栈顶指针、指向下一个任务控制块的指针、任务等待的延迟时间、任务的当前状态标志与任务的优先级别等一些与任务管理有关的属性。
当任务的CPU使用权被剥夺时,μC/OS-Ⅱ用任务控制块来保存该任务的状态,从而保证任务重新获得CPU使用权时从断点处执行。
系统中的每一个任务都有一个任务控制快,用于存放该任务的状态。任务控制块中的各个单元的值一般不需要用户改动。
2.任务管理
任务分为两个形式:
1、无限循环
void Task1()
{for(;;){//用户任务代码}osdelay();
}
2、任务在运行完毕后自我删除
void Task2()
{//用户任务代码//自我删除osTaskDel(OS_PRIO_SELF);osdelay();
}
3、基本的应用任务框架:
int main()
{//目标板初始化OsInit(); //创建任务1、2、3......osTaskCreate(Task1,(void*)0,(os_stk *)&Task1_Stack[TASK_STACK_SIZE - 1],Task1_PRIO);osTaskCreate(Task2,(void*)0,(os_stk *)&Task1_Stack[TASK_STACK_SIZE - 1],Task2_PRIO);//开始任务osStart();//进入调度
}
3.任务状态
μC/OS-Ⅱ任务共有4种状态:
1、睡眠态:任务处于该状态时,仅有任务代码,并没有操作系统处理
2、就绪态:任务处于该状态时,随时可以准备运行
3、运行态:正在运行的任务,一般是运行就绪优先级最高的任务
4、挂起态:当任务处于该状态时,任务不会运行,只有当任务返回就绪状态时,才可以运行
每个任务的切换如下:
uC/OS-II OS_TASK.C中有关任务管理的函数
μC/OS-Ⅱ时间管理
μC/OS-Ⅱ内核要求用户提供定时中断来实现延时和超时控制等功能。此定时中断叫做时钟节拍。时钟节拍频率越高,系统的负载越重。
主要用到的函数:
函数 | 用途 |
---|---|
OSTimeDly() | 任务延时函数 |
OSTimeDlyHMSM() | 按时分延时函数 |
OSTimeDlyResume() | 让处在延时期的任务结束延时 |
OSTimeGet() | 得到系统时间 |
OSTimeSet() | 设置系统时间 |
1、OSTimeDly():
调用该函数会进行一次任务调度,并且执行下一个优先级最高的就绪态任务。
调用函数之后,一旦规定的时间期满或者有其他任务通过调用OSTimeDlyResume()取消延时,它就会立马进入就绪状态。
本函数是将处于运行状态的转入挂起状态,当延时结束的时候,任务重新返回到就绪状态。
2、OSTimeDlyResume()
使用任务延迟函数OSTimeDly()和任务挂起函数OSTaskSuspend()的任务都是处于挂起状态,但是两个处理方法不同:
任务的时间延时与恢复是通过设置任务控制块TCB中的OSTCBDly表示。
OSTaskSuspend通过设置任务控制块TCB中的OS_STAT_SUSPEND标志来表示任务正在被挂起。
所以通过任务回复函数OSTaskResume()不能恢复处于被时间延时的任务
μC/OS-Ⅱ内存管理
malloc和free函数可以动态分配内存和释放内存,但是多次这样做会把原来很大的一块连续内存区域逐渐地分割成许多非常小而且彼此不相邻的内存区域,也就是内存碎片
操作系统把连续的大块内存按照分区:
μC/OS-Ⅱ对malloc和free进行了改进,使它们可以分配和释放固定大小的内存块。用户应用程序可以从不同的内存分区中得到不同大小的内存块,特定的内存块在释放时必须重新放回它以前所属于的内存分区。使用这样的内存管理算法,内存碎片问题得以解决。
内存控制块MCB
内存控制块MCB是一个数据结构,μCOSII用它对内存进行管理,来表示每个内存分区的信息,系统中的每个内存分区都有它自己的内存控制块,MCB包括以下内容:
μC/OS-Ⅱ任务通信
1.事件
μC/OS-Ⅱ提供三种特殊的数据共享和任务通信的方法:信号量、邮箱、消息队列。统称为事件。
μC/OS-Ⅱ用事件控制块ECB表示事件的信息。
任务或中断服务子程序可以通过ECB向另外的任务发事件。
任务可以等待另一个任务或者中断服务子程序给它发事件。
只有任务可以等待事件发生,中断服务子程序不能。
1、对于等待的任务,可指定一个最长等待事件,以免无限期等待。中断服务程序不能等待事件
2、多个任务同时等待一个事件,优先级最高的任务得到该时间并进入就绪态
2.事件控制块ECB
ECB是描述事件的数据结构,它由以下几个部分构成:
- OSEventPtr 当事件是邮箱或者消息队列时,是指向邮箱或消息队列的的指针
2.OSEventCnt 当事件是一个信号量时,是信号量的计数器。
3.OSEventTbl[] 和 .OSEventGrp 等待该事件的任务表和组。
4.OSEventType定义了事件的具体类型,可以是信号量(OS_EVENT_SEM)、邮箱(OS_EVENT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。
3.信号量
信号量由两部分组成:该信号量的计数器的值、等待该信号量的任务组成的等待任务列表。
信号量的接口分为:
建立一个信号量
等待一个信号量
发送一个信号量
无等待地请求一个信号量
查询一个信号量的当前状态
信号量的使用规范:
任何时刻都可以建立信号量
只有任务可以等待信号量
中断和任务可以发送信号量
中断和任务可以无等待地请求一个信号量
只有任务可以查询一个信号量的当前状态
工作原理
在使用一个信号量之前,首先调用函数建立该信号量,对信号量的初始计数值赋值。
如果信号量用来表示一个或者多个事件的发生,那么该信号量的初始值设为0;
如果信号量用来对共享资源的访问,那么该信号量的初始值设为1;
如果信号量是用来表示允许任务访问n个相同的资源,那么初始值应为n,并作为可计数的信号量使用。
任务根据信号量的值来判断是否可以运行(比如是否对公共资源可以访问)
几个任务往往只建立一个信号量sem:
任务一先执行任务二后执行的信号量变化顺序:任务一先P操作信号量排斥任务二的执行,完成后再V操作信号量放行任务二
4.邮箱
邮箱的接口分为:
建立一个邮箱
等待一个邮箱中的消息
发送一个消息到邮箱中
无等待地从邮箱中得到一个消息
查询一个邮箱的状态
邮箱使一个任务或者中断服务子程序向另一个任务发送一个指针型的变量。该指针指向一个包含特定“消息”的数据结构。
如果使用邮箱的目的是通知一个事件的发生(发送一条消息),那么就要初始化该邮箱为NULL,因为在开始时,事件还没有开始。
如果用邮箱来共享某些资源,那么就要初始化该邮箱为一个非NULL的指针,这种情况,邮箱作为一个二值信号使用。
邮箱的使用规范:
1、任何时刻都可以建立邮箱
2、中断和任务可以无等待地请求邮箱的消息
3、中断和任务可以发送消息到消息
4、只有任务可以等待一个邮箱的消息
5、只有任务可以查询一个邮箱的当前状态
6、邮箱中的消息可以被读出
5.消息队列
一个任务通过消息队列向另一个任务发送“一串”指针型的变量,该些指针指向各个包含“消息”的数据结构
消息队列可以看成多个邮箱组成的队列,但这些邮箱使用同一个等待任务列表
消息队列的接口分为:
建立一个消息队列
等待一个消息队列中的消息
发送一个消息到消息队列中
无等待地从消息队列中得到一个消息
查询一个消息队列的状态
清空一个消息队列
消息队列的使用规范:
任何时刻都可以建立消息队列中的消息
只有任务可以等待一个消息队列中的消息
中断和任务可以发送消息到消息队列中
中断和任务可以无等待地请求消息队列中的消息
只有任务可以查询一个消息队列的当前状态
中断和任务可以清空消息队列