就绪优先级为映像响表

在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. centos6-centos7防火墙(iptables-firewalld)设置端口nat转发

    背景: 将本机的 8080端口转发至其他主机,主机 IP:192.168.1.162,目标主机 IP和端口192.168.1.163:80,方法如下: centos6系统iptables环境下: ip ...

  2. WinForm中实现按Enter将光标移动到下一个文本框

    首先窗体加载出来是上面这个样子.有五个文本框,我们要实现的功能就是输入姓名后按Enter,使光标直接定位到手机号中. 在页面加载的时候我们就要获取所有文本框控件,并添加回车事件 private voi ...

  3. testng学习笔记-- beforesuit和aftersuit

    一.定义 测试套件,主要关注执行顺序 套件:suit可以包含多个class 二.代码标签 三.运行结果

  4. 为了更好的多线程性能,在对象创建或者更新时,若数据大于2047字节则 Python 的 GIL 会被释放。 执行计算密集型任务如压缩或哈希时释放 GIL

    hashlib - Secure hashes and message digests - Python 3.8.3 documentation https://docs.python.org/3.8 ...

  5. FridaHook框架学习(1)

    FridaHook框架学习(1) 前言 本次学习过程参考https://bbs.pediy.com/thread-227232.htm Frida安装与使用 Windows端安装 pip instal ...

  6. Docker+Prometheus+Alertmanager+Webhook钉钉告警

    Docker+Prometheus+Alertmanager+Webhook钉钉告警 1.环境部署 1.1 二进制部署 1.2 docker部署 1.2.1 webhook 1.2.2 alertma ...

  7. JVM 线上故障排查

    JVM 线上故障排查 Linux 1.1 CPU 1.2 内存 1.3 存储 1.4 网络 一.CPU 飚高 寻找原因 二.内存问题排查 三.一般排查问题的方法 四.应用场景举例 4.1 怎么查看某个 ...

  8. CS229 Lecture 02

    最近忙成狗,各种意义上.第二章其实之前已经看过了但是已经完全忘记了,于是重新看了一遍当复习. 判别学习算法:直接学习$p(y|x)$,或学习一个假设$h_{\theta}(x)$输出结果 生成学习算法 ...

  9. hadoop的Namenode HA原理详解

    为什么要Namenode HA? 1. NameNode High Availability即高可用. 2. NameNode 很重要,挂掉会导致存储停止服务,无法进行数据的读写,基于此NameNod ...

  10. 查看Linux用的桌面是GNOME、KDE或者其他(转)

    http://superuser.com/questions/96151/how-do-i-check-whether-i-am-using-kde-or-gnome 1) pgrep -l &quo ...