就绪优先级为映像响表

在UCOSIII内,任务调度是要先找到优先级最高的任务,然后执行。理论上对于UCOSIII可以有无数个优先级,每个优先级又可以有无数个任务但是对于这么多的任务如何快速查到到当先就绪的最高优先级的任务是那个,为了完成这个功能ucos的设计了就绪优先级为映像响表组合任务就绪表来实现这个功能。对于32位的CPU就绪优先级为映像响表的数据结构如下:

数组元素

31

30

29

......

2

1

0

OSPrioTbl[0]

优先级0

优先级1

优先级2

...

优先级29

优先级30

优先级31

OSPrioTbl[1]

优先级32

33

34

...

61

62

63

OSPrioTbl[2]

...

...

...

...

...

...

...

......

...

...

...

...

...

...

...

OSPrioTbl[n]

...

...

...

...

...

...

...

相关的函数有查找就绪优先级为映像响表中最高的优先级:

OS_PRIO  OS_PrioGetHighest (void)
{
CPU_DATA *p_tbl;
OS_PRIO prio; prio = (OS_PRIO)0;
p_tbl = &OSPrioTbl[0];
while (*p_tbl == (CPU_DATA)0) { /* Search the bitmap table for the highest priority */
prio += DEF_INT_CPU_NBR_BITS; /* Compute the step of each CPU_DATA entry */
p_tbl++;
}
prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl); /* Find the position of the first bit set at the entry */
return (prio);
}

这里因为STM32提供了计算前导零的指令所以程序中直接调用了汇编函数CPU_CntLeadZeros来计算前导零,对于不支持这个指令的CPU程序中也给出了C计算前导零的函数.

CPU_CntLeadZeros
CLZ R0, R0 ; Count leading zeros
BX LR

C计算前导零(32bit)的函数.

#if (CPU_CFG_DATA_SIZE_MAX >= CPU_WORD_SIZE_32)
CPU_DATA CPU_CntLeadZeros32 (CPU_INT32U val)
{
#if (!((defined(CPU_CFG_LEAD_ZEROS_ASM_PRESENT)) && \
(CPU_CFG_DATA_SIZE >= CPU_WORD_SIZE_32)))
CPU_DATA ix;
#endif
CPU_DATA nbr_lead_zeros; /* ---------- ASM-OPTIMIZED ----------- */
#if ((defined(CPU_CFG_LEAD_ZEROS_ASM_PRESENT)) && \
(CPU_CFG_DATA_SIZE >= CPU_WORD_SIZE_32))
nbr_lead_zeros = CPU_CntLeadZeros((CPU_DATA)val);
nbr_lead_zeros -= (CPU_CFG_DATA_SIZE - CPU_WORD_SIZE_32) * DEF_OCTET_NBR_BITS; #else /* ----------- C-OPTIMIZED ------------ */
if (val > 0x0000FFFFu) {
if (val > 0x00FFFFFFu) { /* Chk bits [31:24] : */
/* .. Nbr lead zeros = .. */
ix = (CPU_DATA)(val >> 24u); /* .. lookup tbl ix = 'val' >> 24 bits */
nbr_lead_zeros = (CPU_DATA)(CPU_CntLeadZerosTbl[ix] + 0u); /* .. plus nbr msb lead zeros = 0 bits.*/ } else { /* Chk bits [23:16] : */
/* .. Nbr lead zeros = .. */
ix = (CPU_DATA)(val >> 16u); /* .. lookup tbl ix = 'val' >> 16 bits */
nbr_lead_zeros = (CPU_DATA)(CPU_CntLeadZerosTbl[ix] + 8u); /* .. plus nbr msb lead zeros = 8 bits.*/
} } else {
if (val > 0x000000FFu) { /* Chk bits [15:08] : */
/* .. Nbr lead zeros = .. */
ix = (CPU_DATA)(val >> 8u); /* .. lookup tbl ix = 'val' >> 8 bits */
nbr_lead_zeros = (CPU_DATA)(CPU_CntLeadZerosTbl[ix] + 16u); /* .. plus nbr msb lead zeros = 16 bits.*/ } else { /* Chk bits [07:00] : */
/* .. Nbr lead zeros = .. */
ix = (CPU_DATA)(val >> 0u); /* .. lookup tbl ix = 'val' >> 0 bits */
nbr_lead_zeros = (CPU_DATA)(CPU_CntLeadZerosTbl[ix] + 24u); /* .. plus nbr msb lead zeros = 24 bits.*/
}
}
#endif return (nbr_lead_zeros);
}
#endif

CPU_CntLeadZeros32 ()

同样8,16,64,位的都是相同的原理。

置位表中某个优先级处于就绪

void  OS_PrioInsert (OS_PRIO  prio)
{
CPU_DATA bit;
CPU_DATA bit_nbr;
OS_PRIO ix; ix = prio / DEF_INT_CPU_NBR_BITS;
bit_nbr = (CPU_DATA)prio & (DEF_INT_CPU_NBR_BITS - 1u);
bit = 1u;
bit <<= (DEF_INT_CPU_NBR_BITS - 1u) - bit_nbr;
OSPrioTbl[ix] |= bit;
}

表中某个优先级处于就绪位清零:

void  OS_PrioRemove (OS_PRIO  prio)
{
CPU_DATA bit;
CPU_DATA bit_nbr;
OS_PRIO ix; ix = prio / DEF_INT_CPU_NBR_BITS;
bit_nbr = (CPU_DATA)prio & (DEF_INT_CPU_NBR_BITS - 1u);
bit = 1u;
bit <<= (DEF_INT_CPU_NBR_BITS - 1u) - bit_nbr;
OSPrioTbl[ix] &= ~bit;
}

系统巧妙地设计了就绪优先级位映像表数据结构就是为了能够快速的查找到就绪列表中最高的优先级任务,同时搭配就绪列表就可以方便管理任务的调用了。

任务就绪列表
这是一个用来管理UCOS内就绪任务的列表,系统定义了一个数组OSRdyList[OS_CFG_PRIO_MAX],每个优先级对应其中一个元素,反过来说就是一个元素管理同一个优先级下的所有任务,OSRdyList的数据结构如图,很简单HeadPtr指向优先级上的第一个任务控制块,TailPtr指向最后一个,NbrEnries记录任务及上有多少任务。任务控制块里的NextPtr和PrePtr相互之间串成一个双向链表。如图:

初始化任务就绪表函数其实就是对OSRdyList[]每个结构体进行0值初始化。

使任务就绪函数其中OS_PrioInsert是将就绪优先级为映像响表对应位置1的操作。OS_RdyListInsertTail是将任务插入同优先级的就绪表的末尾,OS_RdyListInsertHead为插入开头,OS_RdyListMoveHeadToTail是将同优先级的任务的开头的任务移到末尾的操作,在时间轮片调度处调用。

void  OS_RdyListInsert (OS_TCB  *p_tcb)
{
OS_PrioInsert(p_tcb->Prio);
if (p_tcb->Prio == OSPrioCur) { /* Are we readying a task at the same prio? */
OS_RdyListInsertTail(p_tcb); /* Yes, insert readied task at the end of the list */
} else {
OS_RdyListInsertHead(p_tcb); /* No, insert readied task at the beginning of the list */
}

最后看到OSStart()函数内的一段代码就知道,系统内核是如何调度一个高优先级的任务开始运行的。

if (OSRunning == OS_STATE_OS_STOPPED) {
OSPrioHighRdy = OS_PrioGetHighest(); /* Find the highest priority */
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;
OSTCBCurPtr = OSTCBHighRdyPtr;
OSRunning = OS_STATE_OS_RUNNING;
OSStartHighRdy(); /* Execute target specific code to start task */
*p_err = OS_ERR_FATAL_RETURN; /* OSStart() is not supposed to return */

其中OSStartHighRdy()为一个汇编函数是任务管理相关的函数,就知道他的功能就是开始当前最高优先级的任务的运行就可以。

OSStartHighRdy
LDR R0, =NVIC_SYSPRI14 ; Set the PendSV exception priority
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0] MOVS R0, #0 ; Set the PSP to 0 for initial context switch call
MSR PSP, R0 LDR R0, =OS_CPU_ExceptStkBase ; Initialize the MSP to the OS_CPU_ExceptStkBase
LDR R1, [R0]
MSR MSP, R1 LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0] CPSIE I ; Enable interrupts at processor level OSStartHang
B OSStartHang ; Should never get here

OSStartHighRdy

任务轮片调度:

在创建任务时设置任务轮片的时间节拍数,当任务执行到一定的时钟节拍后就会将任务同优先级的任务进行调度运行,这个函数在系统时钟的中断函数里有调用。

void  OSTimeTick (void)
{
OS_ERR err;
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
CPU_TS ts;
#endif OSTimeTickHook(); /* Call user definable hook */ #if OS_CFG_ISR_POST_DEFERRED_EN > 0u ts = OS_TS_GET(); /* Get timestamp */
OS_IntQPost((OS_OBJ_TYPE) OS_OBJ_TYPE_TICK, /* Post to ISR queue */
(void *)&OSRdyList[OSPrioCur],
(void *) 0,
(OS_MSG_SIZE) 0u,
(OS_FLAGS ) 0u,
(OS_OPT ) 0u,
(CPU_TS ) ts,
(OS_ERR *)&err); #else (void)OSTaskSemPost((OS_TCB *)&OSTickTaskTCB, /* Signal tick task */
(OS_OPT ) OS_OPT_POST_NONE,
(OS_ERR *)&err); #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
OS_SchedRoundRobin(&OSRdyList[OSPrioCur]);
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
void OS_SchedRoundRobin (OS_RDY_LIST *p_rdy_list)
{
OS_TCB *p_tcb;
CPU_SR_ALLOC(); if (OSSchedRoundRobinEn != DEF_TRUE) { /* Make sure round-robin has been enabled */
return;
} CPU_CRITICAL_ENTER();
p_tcb = p_rdy_list->HeadPtr; /* Decrement time quanta counter */ if (p_tcb == (OS_TCB *)0) {
CPU_CRITICAL_EXIT();
return;
} if (p_tcb == &OSIdleTaskTCB) {
CPU_CRITICAL_EXIT();
return;
} if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {
p_tcb->TimeQuantaCtr--;
} if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { /* Task not done with its time quanta */
CPU_CRITICAL_EXIT();
return;
} if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) { /* See if it's time to time slice current task */
CPU_CRITICAL_EXIT(); /* ... only if multiple tasks at same priority */
return;
} if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Can't round-robin if the scheduler is locked */
CPU_CRITICAL_EXIT();
return;
} OS_RdyListMoveHeadToTail(p_rdy_list); /* Move current OS_TCB to the end of the list */
p_tcb = p_rdy_list->HeadPtr; /* Point to new OS_TCB at head of the list */
if (p_tcb->TimeQuanta == (OS_TICK)0) { /* See if we need to use the default time slice */
p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;
} else {
p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; /* Load time slice counter with new time */
}
CPU_CRITICAL_EXIT();
}

OS_SchedRoundRobin ()

轮片调度就是将同一优先级就绪列表的的任务从头到尾的轮换执行。

μC/OS-III---I笔记11---就绪任务列表管理的更多相关文章

  1. Python3+Selenium3+webdriver学习笔记11(cookie处理)

    #!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记11(cookie处理)'''from selenium im ...

  2. 机器学习实战 - 读书笔记(11) - 使用Apriori算法进行关联分析

    前言 最近在看Peter Harrington写的"机器学习实战",这是我的学习心得,这次是第11章 - 使用Apriori算法进行关联分析. 基本概念 关联分析(associat ...

  3. Ext.Net学习笔记11:Ext.Net GridPanel的用法

    Ext.Net学习笔记11:Ext.Net GridPanel的用法 GridPanel是用来显示数据的表格,与ASP.NET中的GridView类似. GridPanel用法 直接看代码: < ...

  4. SQL反模式学习笔记11 限定列的有效值

    目标:限定列的有效值,将一列的有效字段值约束在一个固定的集合中.类似于数据字典. 反模式:在列定义上指定可选值 1. 对某一列定义一个检查约束项,这个约束不允许往列中插入或者更新任何会导致约束失败的值 ...

  5. uc/os iii移植到STM32F4---IAR开发环境

    也许是先入为主的原因,时钟用不惯Keil环境,大多数的教程都是拿keil写的,尝试将官方的uc/os iii 移植到IAR环境. 1.首先尝试从官网上下载的官方移植的代码,编译通过,但是执行会报堆栈溢 ...

  6. JAVA自学笔记11

    JAVA自学笔记11 1:Eclipse的安装 2:用Eclipse写一个HelloWorld案例,最终在控制台输出你的名字 A:创建项目 B:在src目录下创建包.cn.itcast C:在cn.i ...

  7. 基于μC/OS—III的CC1120驱动程序设计

    基于μC/OS—III的CC1120驱动程序设计 时间:2014-01-21 来源:电子设计工程 作者:张绍游,张贻雄,石江宏 关键字:CC1120   嵌入式操作系统   STM32F103ZE   ...

  8. golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好

    golang学习笔记11   golang要用jetbrain的golang这个IDE工具开发才好  jetbrain家的全套ide都很好用,一定要dark背景风格才装B   从File-->s ...

  9. Spring MVC 学习笔记11 —— 后端返回json格式数据

    Spring MVC 学习笔记11 -- 后端返回json格式数据 我们常常听说json数据,首先,什么是json数据,总结起来,有以下几点: 1. JSON的全称是"JavaScript ...

随机推荐

  1. Socket.IO基础教程

    什么是Socket.IO Socket.IO是一个库,可用于在浏览器和服务器之间进行实时,双向和基于事件的通信.它包括: 使Node.js服务器:来源 | API 为浏览器(可从Node.js的也运行 ...

  2. [阿里DIEN] 深度兴趣进化网络源码分析 之 Keras版本

    [阿里DIEN] 深度兴趣进化网络源码分析 之 Keras版本 目录 [阿里DIEN] 深度兴趣进化网络源码分析 之 Keras版本 0x00 摘要 0x01 背景 1.1 代码进化 1.2 Deep ...

  3. JavaScript中的构造函数和原型!

    JavaScript中的原型! 原型的内容是涉及到JavaScript中的构造函数的 每一个构造函数都有一个原型对象!prototype 他的作用是 共享方法!还可以扩展内置对象[对原来的内置对象进行 ...

  4. 蓝 / 绿部署(Blue/Green) 金丝雀发布(Canary Release) 功能标记(Feature Flagging)

    https://www.cnblogs.com/apanly/p/8784096.html 最终,我选择了 GraphQL 作为企业 API 网关 蓝 / 绿部署(Blue/Green) 金丝雀发布( ...

  5. Privacy-Enhanced Mail (PEM) Privacy Enhancement for Internet Electronic Mail

    小结 1. 加密基本流程 本地格式标准格式认证(填充与完整性检查)与加密可打印编码 Privacy-Enhanced Mail (PEM) RFC 2313 - PKCS #1: RSA Encryp ...

  6. Java——单例模式、多线程

    单例模式 单例模式练习 单例模式的分类 懒汉式 懒汉式相关练习 饿汉式 饿汉式相关练习 线程安全 使用双重检测机制实现线程安全的懒汉式 使用静态内部类实现线程安全的单例模式 多线程 多线程的三种方式 ...

  7. UML——宏观总结

    今天果断开始UML的学习,要不就要被12期赶超了.努力学习的效率 一.宏观导图把控 导图概要说明:RUP这块儿的内容相当于软件工程已经学过了,只不过这里换了个名词而已.面向对象,已经不再陌生,vb中早 ...

  8. TypeScript 入门教程学习笔记

    TypeScript 入门教程学习笔记 1. 数据类型定义 类型 实例 说明 Number let num: number = 1; 基本类型 String let myName: string = ...

  9. docker(11)Dockerfile 中的COPY与ADD 命令

    前言 Dockerfile 中提供了两个非常相似的命令 COPY 和 ADD,本文尝试解释这两个命令的基本功能,以及其异同点,然后总结其各自适合的应用场景. Build 上下文的概念 在使用 dock ...

  10. Hive之insert和insert overwrite

    1. hive 表及数据准备 建表,并插入初始数据.向表中插入 hive> use test; hive> create table kwang_test (id int, name st ...