学习-嵌入式实时操作系统uCOS-II

参考书:
《STM32F1 FreeRTOS开发手册_V1.1》正点原子
《STM32F1开发指南-库函数版本_V3.3》正点原子
《嵌入式实时操作系统uCOS-II》(第二版)
《嵌入式实时操作系统uCOS-II原理及应用》任哲

1 简介

uCOS-II实时操作系统
实时操作系统与分时操作系统的区别:分时操作系统将时间一块一块的分给不同的任务。
实时操作系统提供给用户三样东西:内存管理、多任务管理调度、外围资源管理。主要是提供内核,外围很多东西用户自己写。
uCOS-II的内核是可剥夺型的,也就是优先级任务会打断低优先级任务的执行。
可重入函数:可以被多个任务调用的,可重入函数都是使用局部变量,如果使用全局变量则应该有一定的策略来保护全局变量。

2 uCOS-II中的任务

操作系统的内核的主要工作就是对任务进行管理和调度。
一个任务相当于一个死循环,相当于一个线程。
这个操作系统如何管理任务:任务控制块TCB。
这个操作系统的任务分为系统任务和用户任务,系统任务是系统内核自带的,提供系统正常工作的保障。
uCOS-II系统设置了两个系统任务,空闲任务和统计任务。Cpu空闲状态下执行空闲任务。目前任务必须存在。统计任务就是统计一下在单位时间内CPU的使用时间,也叫CPU占用率。统计任务可以通过设置使它不存在。

在这里插入图片描述
任务的状态:睡眠状态、就绪状态、运行状态、等待状态、中断服务状态。
任务相当于一个线程,函数负责创建和启动这些任务,操作系统负责管理调度这些任务。
uCOS-II系统使用优先级来表明系统任务,每个任务的优先级必须不一样,老版本中最多支持64个任务,新版本可以有256个,空闲任务优先级倒数第一,统计任务优先级倒数第二,系统任务已经占用了两个优先级。优先级素质越大则优先级越低,优先级为0的最优先执行。
任务堆栈:每个任务都有自己的人物堆栈,相当于申请了一块线程的内存资源。
任务控制块:相当于是每一个任务的ID,操作系统通过任务控制块来获取任务的全部信息,并调度它们。
调度器:操作系统的核心算法,采用一定算法去调做任务,做到让每个任务都能够执行到,而且每个任务还能根据轻重缓急来执行,有各式各样的算法,windows系统的任务调度算法非常复杂,uCOS-II系统比较简单。
uCOS-II系统的任务调度器会根据 任务就绪表 OSRdyTbl[7]来调度任务。任务就绪表的位置和优先级别的数字成一一对应关系。
uCOS-II系统使用OSTaskCreate() //创建普通任务
uCOS-II系统使用OSTaskCreateExt() //创建扩展任务
uCOS-II系统使用OSTaskSuspend()刮起任务自己或者别的任务。挂起后进入等待状态。

uCOS-II系统可以通过OSSchedLock()给调度器加锁,通过OSSchedUnlock()给调度器解锁。
uCOS-II系统任务的优先级别不是固定不变的,可以动态改变。通过OSTaskChangePrio()改变。
删除任务(吊销ID):把它的TCB从任务控制快链表中删除,归还给空人物控制块链表,并把任务就绪表中的位置改成0。OSTaskDel()。
其他的任务,如果想要删除某一个任务,最好是提出一个请求,然后由那个被删除任务本身来完成删除任务的善后工作,释放内存、释放占用资源,不然就会造成内存泄漏或者资源泄露,在比较高级的操作系统中,会有专门的线程来不定期检查这种泄露的东西。

任务的状态:
在这里插入图片描述

3 中断与时钟

临界段:关了中断的那一段程序,关闭后别使用系统功能,不然可能系统崩溃。
系统时钟:使用硬件定时器做1ms的定时。
任务的调度:为了让每个任务都得到CPU的使用权,uCOS-II系统规定必须要在每一个任务中调用OSTimeDly()让出使用权。

4 任务的同步与通信

为了避免访问资源冲突,为了避免同一时间访问变量,为了让各个任务协作,会有一些机制。
操作系统我会解决两个问题:一个问题就是如果一个任务在访问一个资源,另外一个任务只能等待他访问释放之后才能继续访问;一个问题是相关的任务,可能在执行上有先后顺序,要等到任务一执行完之后,任务二才能够执行。

信号量:是一类事件,资源被占用的情况。资源被占用则信号量加1,资源被释放则信号量减1。高优先级的任务,如果迟迟等待资源的释放而一直使用着CPU,资源没有释放,则他一直等待,那么就会造成整个功能的崩溃。解决办法就是使用等待任务列表,等待时间过长则强行执行。这种互相等待着对方释放资源的现象也称为死锁。

扫描二维码关注公众号,回复: 11312022 查看本文章

消息邮箱:不同任务之间的通信需要发送数据,这个数据就叫做消息。在内存中开辟一块空间用于消息缓冲区,那么用来管理消息缓冲区的指针的数据结构就叫做消息邮箱。
在这里插入图片描述
消息队列:使用一个队列结构来存放消息邮箱,就可以传递多个消息。
在这里插入图片描述

我们可以使用信号量、消息邮箱、消息队列来描述一个事件,为了能够将他们统一管理,定义了一个数据结构叫做事件控制块。
在这里插入图片描述
任务优先级反转的现象:有一个高优先级任务和一个低优先级任务,低优先级任务在执行的时候占用了某个资源(独自占有,互斥型信号量),这个时候高优先级任务打断了低优先级任务,而低优先级任务还没有释放资源。所以高优先级任务执行过程中,如果需要使用这个资源,又会再次等待,如果超出了等待时间限制,那么这个高优先级任务就会暂停而使得低优先级任务先执行。这个就叫任务优先级反转的现象。当系统共同执行的任务很多的时候,这种现象是很难忍受的,他打破了以优先级高低来抢占执行任务的优势。

独占类资源的占用和没占用是首要考虑的,而不是任务的优先级,这导致了任务优先级反转的现象。解决办法之一就是,将那个需要占用资源的低优先级任务的优先级别提升到最高,使别的任务不能打断他的执行,从而让这个原先是低优先级任务的任务快速使用完资源后释放,释放资源后再将这个任务给他还原到之前的优先级别。

5 信号量集

多个任务协作需要信号量集数据结构。本质上就是多个信号量。
信号集及是一个双向链表结构。

6 内存的动态分配

在c语言中,最终无非就是要实现两个方法:malloc()和free().
uCOS-II系统对内存进行了两级管理,一大片连续的内存空间被分区,然后每个区又分成了若干的内存块。操作系统以区为级别管理内存,任务以内存块为级别申请内存和释放内存。整体的使用情况由 -内存控制快OS_MEM- 这种数据结构来记录。
在这里插入图片描述
为什么要分块儿?
malloc()和free()虽然是连续的,但会造成内存碎片存在。

7 内核裁剪

将系统自带的函数,通过配置,是编译器不对其编译,减小操作系统的体量。

8 其他

代码的临界段:关闭中断的那一段,临界段一定要设置的很小。
uCOS-II系统利用OSSched()来实现任务的调度,OSSched()是临界段代码。

共享数据能够满足互斥条件的几种处理方式:关中断、使用测试并置位指令、禁止做任务切换、利用信号量。根据实际情况处理。

uCOS-II系统任务切换的细节:
uC/OS II有系统时钟,每5毫秒中断一次,执行一次OS_Sched()。OS_Sched ()在系统提供的延时函数里面也有,在任务恢复里面也有,在各种需要他的地方都有调用。他的主要代码如下,把正在执行的任务压入任务堆栈,然后根据优先级去执行新的任务。OS_Sched ()里面调用了OS_TASK_SW(),这个是根据处理器不同的汇编。

*********************************************************************************************************
*                                              SCHEDULER
*
* Description: This function is called by other uC/OS-II services to determine whether a new, high
*              priority task has been made ready to run.  This function is invoked by TASK level code
*              and is not used to reschedule tasks from ISRs (see OSIntExit() for ISR rescheduling).
*
* Arguments  : none
*
* Returns    : none
*
* Notes      : 1) This function is INTERNAL to uC/OS-II and your application should not call it.
*              2) Rescheduling is prevented when the scheduler is locked (see OS_SchedLock())
*********************************************************************************************************
*/

void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register     */
    OS_CPU_SR  cpu_sr = 0u;
#endif



    OS_ENTER_CRITICAL();
    if (OSIntNesting == 0u) {                          /* Schedule only if all ISRs done and ...       */
        if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */
            OS_SchedNew();
            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy     */
#if OS_TASK_PROFILE_EN > 0u
                OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */
#endif
                OSCtxSwCtr++;                          /* Increment context switch counter             */
                OS_TASK_SW();                          /* Perform a context switch                     */
            }
        }
    }
    OS_EXIT_CRITICAL();
}

猜你喜欢

转载自blog.csdn.net/x1131230123/article/details/106485849