UCOSII实时操作系统启动原理和理解
一,UCOS启动过程和原理
当调用 OSStart()时,OSStart()从任务就绪表中找出那个用户建立的优先级最高任务的任务控制块。Start()调用高优先级就绪任务启动函数 OSStartHighRdy()[(2)],这个文件与选择的微处理器有关。函数 OSStartHighRdy()是将任务栈中保存的值弹回到CPU 寄存器中,然后执行一条中断返回指令,中断返回指令强制执行该任务代码。OSStartHighRdy()将永远不返回到OSStart()。
int main(void)
{OSInit(); /* 系统初始化*/ /* 创建主任务*/OSTaskCreate(MainTask, (void *)0, &MainTask_Stk[MainTask_StkSize-1], MainTask_Prio);OSStart(); /* 开始任务调度*/return 0;
}
1,UCOSII系统初始化的工作
(1),Ucos系统初始化函数OSInit(),这个函数的目的就是在整个系统启动之前,初始化所有的变量和数据结构。具体主要完成以下功能:
全局变量初始化就绪任务表初始化空任务控制块初始化事件控制块链表初始化信号量集初始化存储器管理初始化Qs队列控制初始化系统空闲任务初始化系统统计任务初始化
(2),在OSInit()函数中建立空闲任务OS_TaskIdle();这个任务总是处于就绪态的,空闲任务的优先级是设置为最低的。OSInit()系统的初始化程序,作用就是就是初始化UCOSII启动过程的全部变量和一些内存池。
(3),调用OSInit以后,任务控制块缓冲池中有OS_MAX_TASKS个任务控制块,事件控制缓冲区中有OS_MAX_EVENTS个事件控制块,消息队列缓冲池OS_Q中有OS_MAX_QS个消息队列控制块
调用OSInit以后,会调用OSTaskCreate()函数创建至少一个任务,因为到时候,程序的指针SP,会跳出main.c的函数,那么如果不创建一个任务的话,那么程序的指针就会跑飞。
(4),OSStart()函数就作用是进行任务调度,因为我们很快就会跳出main.c的函数,不会再跳进来,所以我们需要进行内部的指针跳出,这个函数记录了我们操作系统是怎么进入多任务的状态的。当OS的状态等于错误的状态的时候,把当前的最高优先级的任务赋值给OS的当前的优先级,最高优先级是通过位图来进行查找的,
void OSStart (void)
{if (OSRunning == OS_FALSE) {OS_SchedNew(); /* Find highest priority's task priority number */OSPrioCur = OSPrioHighRdy;OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */OSTCBCur = OSTCBHighRdy;OSStartHighRdy(); /* Execute target specific code to start task */}
}
(5),OSStartHighRdy()函数:
这个开始函数就是这个地方进行任务的切换的,先进入中断然后把CPU寄存器的值进行切换,从而跳转到最高优先级的任务当中。此函数就是最终的函数,与硬件所在的平台是不一样的。
void OSStartHighRdy()
{DWORD dwID;OSInitTrace(100000);OS_ENTER_CRITICAL();OSTaskSwHook();++OSRunning;OSCtxSwW32Event = CreateEvent(NULL,FALSE,FALSE,NULL);OSCtxSwW32Handle = CreateThread( NULL, 0, OSCtxSwW32, 0, 0, &dwID );SetPriorityClass(OSCtxSwW32Handle,THREAD_PRIORITY_HIGHEST);#ifdef SET_AFFINITY_MASKif( SetThreadAffinityMask( OSCtxSwW32Handle, 1 ) == 0 ) {
#ifdef OS_CPU_TRACEOS_Printf("Error: SetThreadAffinityMask\n");
#endif}
#endifSetThreadPriority(OSCtxSwW32Handle,THREAD_PRIORITY_TIME_CRITICAL);OSTick32Handle = CreateThread( NULL, 0, OSTickW32, 0, 0, &dwID );SetPriorityClass(OSTick32Handle,THREAD_PRIORITY_HIGHEST);#ifdef SET_AFFINITY_MASKif( SetThreadAffinityMask( OSTick32Handle, 1 ) == 0 ) {
#ifdef OS_CPU_TRACEOS_Printf("Error: SetThreadAffinityMask\n");
#endif}
#endifSetThreadPriority(OSTick32Handle,THREAD_PRIORITY_HIGHEST);#ifdef WIN_MM_TICKtimeGetDevCaps(&OSTimeCap, sizeof(OSTimeCap));if( OSTimeCap.wPeriodMin < WIN_MM_MIN_RES )OSTimeCap.wPeriodMin = WIN_MM_MIN_RES;timeBeginPeriod(OSTimeCap.wPeriodMin);OSTickEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);OSTickTimer = timeSetEvent((1000/OS_TICKS_PER_SEC),OSTimeCap.wPeriodMin,(LPTIMECALLBACK)OSTickEventHandle, dwID,TIME_PERIODIC|TIME_CALLBACK_EVENT_SET);
#endifSS_SP=(OS_EMU_STK*) OSTCBHighRdy->OSTCBStkPtr; /* OSTCBCur = OSTCBHighRdy;*/ /* OSPrioCur = OSPrioHighRdy; */ResumeThread(SS_SP->Handle);OS_EXIT_CRITICAL();WaitForSingleObject(OSCtxSwW32Handle,INFINITE);#ifdef WIN_MM_TICKtimeKillEvent(OSTickTimer);timeEndPeriod(OSTimeCap.wPeriodMin);CloseHandle(OSTickEventHandle);
#endifCloseHandle(OSTick32Handle);CloseHandle(OSCtxSwW32Event);
}
2,UCOSII是可以实现时间片的轮询
(1),UCOSII本身是不支持同优先级有多个任务的,UCOSIII是支持的,所以实现这个机制的方案就是刚刚事件控制块的灵活使用。
(2),位图是指向某一个任务的,但是UCOSIII的位图是指向一个队列,在同一个队列中优先级相同,也就是说,同一优先级的任务应该是按时间片轮询的方式的。
(3),每个处理器中都会有一个时钟节拍,在时钟节拍中调用任务切换的核心函数,在同一个优先级不断的进行轮询即可实现时间片轮询。
3,系统临界段:
在系统运行过程中,有时候某段代码是不能被打断的,而中断的时机经常会比较随机,为了解决这个问题,提出了临界段的概念。三种解决方式:(1),开关中断的方式 ;(2),通过保存程序状态字并关中断的方式;(3),保存程序状态字到cpu_sr变量中
二,基础理解
1,ucos系统的任务表示:
需要在里面添加中断的代码的,以便于它能跳转到其他的任务中执行,切换任务的时候,判断优先级进行任务之间的切换的工作。
void task1(void * param)
{
for( ; ;)
{
//用户编写的任务代码
//不需要返回的无类型函数
}
}
2,UCOS系统代码结构
三,UCOSS实时操作系统基本功能:
1,内存管理:
内存管理主要是动态内存的管理,当应用程序需要使用内存的时候,可以利用操作系统所提供内存分配函数来获得足够的内存空间。,动态内存就是指malloc、free函数的分配,就是从堆里面拿内存,因为我们本来嵌入式系统的内存分配就比较少,所以这里很关键。将所有已经分配好的空间计算好,利用全局变量分配到静态的代码空间中。实际使用时调用自己编写好的内存管理函数从这块静态空间中申请内存。内存的总体大小是受控的,所有涉及到动态内存的地方实际上都已经被预先分配好,在编译时候写在静态代码区。
MMUType MMUPool[MMU_BUFF_MAX]; //Store all the MMUs
MMUType *MMUFree; //Pointer to the available MMUType in MMUPool
MMUType *MMUBuff8; //Controller of Buff8U
MMUType *MMUBuff16; //Controller of Buff16U
所有需要实际存储的指针都由数组来保存,所有指针最终指向一个实际的变量,诺要使用这种内存管理,第一步要做的就是初始化MMUPOOL初始化内存池。调用MEMInit()之后将内部所有的成员形成一个链表,由MMUFree指向最开头的一个。
这样一来就可以为内存管理块分配一个实际的对象了,调用MemCreate()可以分配一个实际的对象给内存管理块,并且将内存管理与实际的内存存储去联系在一起。
2,多任务管理:
裸机就是一个单任务的前后台的程序,就是一个任务加上中断的机器的实现方法。程序设计人员就可以按照多线程来设计自己的程序,程序的耦合性和单元测试方面就会比较的容易。创建任务的时候,是将任务的函数名,传递给任务堆栈,然后任务堆栈再传到CPU的SR寄存器中,实现任务切换的。所谓的指向任务的指针:其实就是任务的函数的函数名。下面是
(1)任务控制块是来管理任务的。
(2)UCOSII把所有的任务都是通过双向链表来连接到一起的(在整个操作系统中,因为我们不知道用户到底需要多少个任务,所以使用链表的话,在编译后才确认的话,数组的方式优秀很多)
3,外围资源管理:
除了本身自己必须需要的东西,内存和CPU,还有很多输入型设备和输出型设备需要管理。
由于资源是有限的,因此操作系统必须对这些资源进行合理的调度和管理,才能保证每个要使用
资源的任务在运行时可以获得足够的资源。
四,UCOSII操作系统任务之间通信
UCOSII的任务是一个特殊的函数,没有类型,没有返回值,是一个死循环。它之所以的话,在用户需要添加的代码中一定是有一个任务切换的函数调用的,它能够跳出来跟别的任务进行通信。实质上是CPU的SR寄存器中的任务堆栈的切换的过程。
linux和ucos任务进行通信对比异同:
Linux的进程间通信:信号量(互斥型信号量)、消息队列、共享内存、消息邮箱、事件标志组。
UCOSII任务之间进行通信的方式:除了共享内存,其余都存在。
其中有好几个是类型的:比如信号量和互斥型信号量。使用这个是可以占用资源,或者同步任务的运行。比如消息邮箱和消息队列,消息队列也称为多个消息邮箱,都是用来在任务之间传递数据的。事件标记组:首先请求事件标记组的时候,通过一个整型数的某几个位,如果那几个位都已经置位或者都是为空的话,那么请求事件标记组的任务可以得到运行。
由于会产生任务优先级的反转问题,所以需要互斥型信号量。
什么是优先级反转?高优先级任务被低优先级任务剥脱CPU的使用权。
(1)假设现在有一个低的优先级任务占有CPU的内核,同时他占有了一些资源。
(2)此时,来了一个高优先级的任务,它想要得到这个资源,但是这个资源已经被低优先级的任务占有了。
(3)在后来来了一个中等优先级的任务,它因为优先级比低优先级的任务高,但是它的优先级没有高的优先级高,所以它强占CPU的使用权,继续执行。
(4)形成的情况就是中等优先级的任务比高优先级的任务更加早的执行,形成优先级反转的问题。
解决方案A:
如果低优先级任务占有的那个资源后,后面有更高的优先级任务到来的话,那么就把自己的优先级提高到那个想要占有优先级的任务。
解决方案B:
使用互斥型信号量。因为互斥型信号量,只有你自己请求,自己释放,不能通过别的任务释放,如果你请求一个已经请求过的互斥性信号量的话,那么直接跳过执行。
五,UCOSII操作系统运行状态:前四个状态不可缺少
1,睡眠态:首先我们的任务是按需分配的,你想要多少个任务的话,那么我们就可以创建多少个任务。刚刚创建的任务是处于睡眠的状态的。
2,就绪状态:如果系统为任务配备了任务控制块并且在任务的就绪表中进行了就绪登记的话,则任务就具备了运行的充分条件,这时候任务的状态就叫做就绪态。
3,运行状态:处于就绪状态的任务如果经过UCOSII的位图机制,判断为处于最高优先级的任务的话,那么它就可以获得CPU的使用权,这时候就是运行状态。
4,中断服务状态:这个真的就没有什么好说的了,连前后台系统都会存在的状态,触发到中断的条件,就会进入中断态,而且不管你是否处于运行态还是怎么样?
5,等待状态:这个状态的话,是可以通过裁剪UCOSII内核去掉的,其实这个状态就是为了满足任务之间的通讯和任务与中断服务子程序通信产生的一种状态。
UCOSII实时操作系统启动原理和理解相关推荐
- Docker镜像原理学习理解
Docker镜像原理学习理解 一.Docker镜像的组成 1.Docker镜像图层 2.union file system 3.镜像层-bootfs 4.镜像层-rootfs 5.镜像层-依赖环境 6 ...
- 课时3.浏览器访问网页原理(理解)
浏览器访问网页原理(理解) 第一次打开IE6,发现系统自动生成了一个文件夹,所以我们可以得出这个文件夹必然和IE6有一定的关系 先删除Internet Cache下的所有文件夹,然后通过IE6打开百度 ...
- [react] 说说你对React的渲染原理的理解
[react] 说说你对React的渲染原理的理解 1.单向数据流.React是一个MVVM框架,简单来说是在MVC的模式下在前端部分拆分出数据层和视图层.单向数据流指的是只能由数据层的变化去影响视图 ...
- java 线程池的理解_JAVA线程池原理的理解
线程池原理基础理解: 线程池初始化规定个数的线程,然后这些线程一直运行,并且监控线程队列,只要线程队列被添加进线程,那么线程池不断从队列中取出线程运行.直到队列中的线程为空.实例代码如下: packa ...
- android 开发art,Android应用开发之Android 系统启动原理(art 虚拟机)
本文将带你了解Android应用开发之Android 系统启动原理(art 虚拟机),希望本文对大家学Android有所帮助. Android 系统启动原理(art 虚拟机) 一.虚拟机的启动 A ...
- uart怎么判断帧错误_UART通讯总线工作原理的理解
奥的斯电梯OCSS/LCBII /TCBC/GECB板与电梯轿厢和电梯井道之间的串行通讯采用了UART通讯,将井道和轿厢的输入.输出和开关部件的信号转换成串行通讯信号传输给电梯操作控制系统,大大节省了 ...
- uart怎么判断帧错误_UART通讯总线工作原理的理解--龚玉山
奥的斯电梯OCSS/LCBII /TCBC/GECB板与电梯轿厢和电梯井道之间的串行通讯采用了UART通讯,将井道和轿厢的输入.输出和开关部件的信号转换成串行通讯信号传输给电梯操作控制系统,大大节省了 ...
- 【生信】第一二三代测序技术原理的理解
[生信]第一二三代测序技术原理的理解 本文部分图片来源网络或学术论文,文字部分来源网络与学术论文,仅供学习使用. 目录 [生信]第一二三代测序技术原理的理解 1.了解什么是DNA测序,什么是RNA测序 ...
- 【控制】拉普拉斯拉氏变换原理分解理解
扩展链接: [控制]Z变换及其原理讲解 [Matlab 控制] 拉氏变换和Z变换 拉氏变换原理分解理解 拉氏变换原理分解理解 1. 拉普拉斯变换 2. 拉普拉斯逆变换 (1)反演公式 (2)查表法(分 ...
- 从tcp原理角度理解Broken pipe和Connection reset by peer的区别
从tcp原理角度理解Broken pipe和Connection reset by peer的区别 May 20th, 2014 以前我们经常会碰到Broken pipe或者Connection ...
最新文章
- JAVA构造器的实际使用
- 前端笔记之NodeJS(四)MongoDB数据库Mongoose自制接口MVC架构思想|实战
- Vue 用户管理后台思维导图
- linux 提取cpio_15. Linux提取RPM包文件(cpio命令)详解
- PAT甲题题解-1070. Mooncake (25)-排序,大水题
- PHP中巧用curl 并发减少获取第三方网页内容时间
- 2018年9月份计算机二级java无忧模拟软件破解版
- 关于WAMP出现无法启动的一些分析以及解决思路
- IIC上拉电阻的注意事项
- Writeup for 0CTF2017 web
- Arduino和LabVIEW射频校园火警系统
- Consolidation of Unorganized Point Clouds for Surface Reconstruction(WLOP)的代码
- proxy_pool开源项目攻克学习2:服务器架设
- CALIPSO Quality Statements Lidar Level 2 Cloud and Aerosol Layer Products
- funny_upload
- linux 内核printk 打印信息查询方法
- 简明扼要的HDFS元数据管理机制描述(NameNode和Secondary NameNode工作机制)
- 再论推翻百年集论的真扩集定理(2165字)
- math-常见导数公式
- C语言——日期转换为天数