UCOS-II学习笔记

       在操作系统中操作系统UCOS-II比较常见,系统梳理UCOS-II系统架构,文件之间关系,程序执行逻辑。UCOS-II抢占式内核,优先级高的任务优先运行,可以剥夺低优先级任务运行。学习任哲《嵌入式实时操作系统UCOS-II原理及应用》,本书比较浅显,适合作为入门教材、下面在学习感觉比较重要的知识点,有一些知识点看了有豁然开朗的感觉, 解答了自己的困惑


一、任务创建

任务创建:配置任务优先级,堆栈大小等
任务切换:任务调度器完成多任务的切换
任务所占空间大小计算
任务堆栈的确定:
任务的启动运行暂停和终止
任务控制块:保存任务属性、任务堆栈保存任务工作环境 任务程序代码是任务的执行部分
UCOS-II支持64个任务
操作系统提供内存管理、多任务管理、外围资源管理。
临界代码段:不可以被打断,一直执行
OSStart()为操作系统UCos-II提供的启动个任务的函数,使用函数OSStart()启动了各个任务之后,任务交由操作系统来管理和调度
系统任务(默认启动空闲任务,没有用户任务时运行)和用户任务
宏OS_ENTER_CRITICAL和OS_EXIT_CRITICAL之间代码不可以被打断,之间代码叫做临界段

优先级数字越小,优先级越高 ucosii任务优先级0-63 空闲任务优先级(OS_LOWEST_PRIO)最低 统计任务优先级(OS_LOWEST_PRIO-1)
任务堆栈:就是在存储器中按数据后进先出(LIFO)的原则组织的连续存储空间,为了满足任务切换和响应中断时保存CPU寄存器中的内容及存储私有数据的需要,每个任务都应该配有堆栈
堆栈的大小
INT8U  OSTaskCreate (void   (*task)(void *p_arg),   //指向任务的指针
                     void    *p_arg,   //  传递给任务的参数
                     OS_STK  *ptos,   // 任务堆栈栈顶的指针 &mytask[matasksize-1],任务堆栈乡下生长,&mytask[0],任务堆栈向上生长
                     INT8U    prio)    //指定任务优先级别的参数
系统调用OSTaskStkInit() 完成任务堆栈初始化
                       void   (*task)(void *p_arg),   //指向任务的指针
                     void    *pdato   //  传递给任务的参数
                     OS_STK  *ptos,   // 任务堆栈栈顶的指针 &mytask[matasksize-1],任务堆栈乡下生长,&mytask[0],任务堆栈向上生长
                     INT16U   opt)    //指定任务优先级别的参数
任务控制块:记录任务的堆栈指针、任务的当前状态、任务的优先级别等一些与任务管理相关的属性的表叫做任务控制块
任务控制块:是一个结构类型数据
任务快控制链表  空任务快控制链表

二、任务切换

优先级和运行时间限制  运行完毕调用任务就绪表中优先级最高的任务执行1任务处于就绪,0任务未就绪
优先级为prio的任务置为就绪状态
OSRdyGcp |=OSMapTbl[prio>>3];
OSRdyTb[prio>>3]|= OSMapTbl[prio&0x07];
优先级为prio的任务脱离就绪
If((OSRdyTbl[prio>>3]&=-OSMapTbl[prio&0x07])==0)

OSRdyGrp&=-OSMapTbl[prio>>3];
从任务就绪表中获取优先级别最高的就绪表(p43))
Y = OSUnMapTal[OSRdyGrp];
X =OSUnMapTAl[OSRdyTbl[y]];
Prio=(y<<3)+x;

Y = OSUnMapTnl[OSRtyGrp];
Prio = (INT8U)((y<<3)+OSUnMapTbl[OSRdyTbl[y]]);
任务的调度(任务级的调度器、中断级的调度器)
在多任务系统中,令CPU中止当前正在运行的任务转而去运行另一个任务的工作叫做任务切换。而按某种规则进行任务切换的工作叫做任务的调度
任务调度器的主要工作:
一、在任务就绪表中查找具有最高优先级别的就绪任务,
二、实现任务的切换
步骤
一、获得待运行任务的TCB指针
二、进行断点数据的切换

调用函数OSSchedLock()和OSSchedUnlock()对调度器上锁和解锁,上锁OSLockNesting就加1,反之减1.
调度器OSSched()在确认未被上锁并且不是中断服务程序调用调度器的情况下,首先从任务就绪表中查找得到最高优先级就绪任务的优先级别OSPrioHighRdy;然后在确认这个就绪任务不是当前正在运行的任务(OSPrioCur是存放在运行任务的优先级别的变量)的条件下。用OSPrioHighRdy作为下标去访问数组OSTCBPrioThl[],把数组元素OSTCBPrioTbl[OSPrioHighRdy]的值(即待运行就绪任务的任务控制块指针)赋给指针变量OSTCBHighRdy,于是下面就可以依据OSTCBHighRdy和OSTCBCur这两个分别指向待运行任务控制块和当前任务控制块的指针在宏OS_TASK_SW()中实现任务切换了


uCOSII的中断进和时钟
中断服务子程序(Interrupt Service Routines ,ISR)
中断向量:中断服务子程序的入口地址

进入中断服务函数 OSInitExit() 
退出中断服务函数OSIntEnter()
延时函数控制任务运行
时钟节拍服务
UCos系统运行时间,节拍计数


三、任务间的同步和通信


共享资源的访问
OSXXXXCreate(请求)    OSXXX Pend ()   OSXXXDele   OXXXPost(发送)
XXX含义
Sem 对信号量进行操作的函数
Mutex 对互斥型信号量进行操作的函数
Mbox 对消息邮箱进行操作的函数
Q 对消息队列进行操作的函数

四种通信方式的函数定义差不多,创建、发送、请求、删除、因此以信号量为例进行讲述。

在UCos—II中使用信号量、邮箱(消息邮箱)、消息队列这些称作事件的中间环节来实现任务间的通信
互斥型信号量使得任务无冲突访问共享资源
定义信号量
代码中判断信号量的值
发信号,让任务获取信号量,获得CPU使用权
设置任务等待信号量的时限,超过时限使等待任务脱离等待状态,继续运行,避免无限等待,进入死机状态。


消息邮箱:在内存创建一个存储空间作为该数据的缓冲区,如果把这个缓冲区叫做消息缓冲区,那么在任务间传递数据(消息)的一个最简单的方法就是传递消息缓冲区的指针,用来传递消息缓冲区指针的数据结构。

消息队列:让数组的每个元素都存放一个消息缓冲区指针,那么任务就可以通过传递这个指针数组指针的方法来传递多个消息了
题外话:
指针数组int  *p[10]    数组内部十个指针
数组指针int  (*p)[10]   指向一个指针,十个数组元素

 

任务进入等待状态
Void OS_EventTaskWait(    OS_EVENT=pevent // 事件控制块的指针 )
等待状态进入就绪状态
INT8U OS_EventTaskRdy(
   OS_EVENT *pevent; //事件控制块的指针
Void *msg;  //未使用
INT8U msk; // 清除TCB状态标识掩码
 使一个等待超时的任务进入就绪状态
Void OS_EventTO(    OS_EVENT *pevent  //事件控制块的指针);
信号量:每当有任务申请信号量时,如果信号量计数器OSEventCnt的值大于0,则把OSEventCnt减1并使任务继续运行,如果OSEventCnt的值为0,则会将任务列入任务等待表OSEventTbl[].而使任务处于等待状态,如果有正在使用信号量的任务释放了该信号量,则会在任务等待表中找出优先级别最高的等待任务,并在使它就绪后调用调用器引发一次调度,如果任务等待表中已经没有等待任务,则信号量计数器就只简单地加1

创建信号量
OSSemCreate( INT8U cnt)
请求信号量  超出等待时限,结束等待状态进入就绪状态
OSSemPend(OS_EVENT *pevent ,  //信号量的指针
            INT16U   timeout, //等待时限 timeout=0 无限长等待
INT8U *err //错误信息



发送信号量
OSSemPost(OS_EVENT *pevent  //信号灯的指针)
函数OSSemPost()在对信号量的计数器操作以前,首先要检查是否还有等待该信号量的任务,如果没有,就把信号量计数器OSEventCnt加1,如果有,则调用调度器OS_Sched()去运行等待任务中优先级别最高的任务。

优先级反转
优先级 A>B>C  AC共用资源D,C在运行,此时A到来,需要等待C运行完后A运行,此时B到来,由于没有使用共享资源,B先于A执行,相当于A的优先级被C拉低了。
解决优先级反转:使得获得信号量任务的优先级别在使用共享资源期间提升到所有任务最高优先级的高一个级别上, 以使该任务不被其他人物所打断,从而能尽快地使用完共享资源并释放信号量,然后在释放信号量之后,在恢复该任务原来的优先级别。
互斥型信号量是一个二值信号量,因此也叫作信号,任务可以使用互斥信号量来实现对共享资源的独占式处理。
创建互斥型信号量
OS_EVENT  *OSMutexCreate( INT8U prio,//优先级别  INT8U *err //错误信息  )

请求互斥信号量
Void OSMutexPend(
OS_EVENT *pevent //互斥型信号量指针
INT16U timeout //等待时限
INT8U  *err  //错误信息

发送互斥型信号量
 INT8U OSMutexPost(OS_EVENT *pevent  //互斥型信号量指针)
获取互斥型信号量的当前状态
INT8U OSMutexQuery(OS_EVENT *pevent,//互斥型信号量指针
   OS_MUTEX_DATA *pdata //存放互斥信号量状态的结构
);
删除互斥型信号量
OS_EVENT *OSMutexDel(
  OS_EVENT *pevent, //互斥型信号量指针
INT8U opt, //删除方式选项
INT8U *err //错误信息);

四、内存的动态分配

内存控制块
操作系统以分区为单位来管理动态内存,而任务以内存块为单位来获得和释放动态内存


内存分区:在内存中划分出来可以动态分配的区域。
内存中划分内存分区与内存块的方法:只要定义一个二维数组即可,其中一维数组就是一个内存块
定义一个用来存储INT16U类型的数据,有10个内存块,每个内存块长度为10的内存分区代码如下  INT16U IntMemBuf[10][10]
注意 :上面这个定义只是在内存中划分出了分区及内存块区域,还不是一个真正可以动态分配的内存区  只有把内存控制块与分区关联起来之后,系统才能对其进行相应的管理和控制,它才能是一个真正的动态内存区。
UCOS-II用于动态内存管理的函数有
创建动态内存分区函数 OSMemCreate()
请求获得内存块函数OSMemGet()
释放内存块函数OSMemPut()
查询动态内存分区状态函数OSMemQuery()
Cos-II要求同一个分区中内存块的字节数必须相等,而且每个分区与该分区内存块的数据类型必须相同

 

 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值