μC/OS-III---I笔记11---就绪任务列表管理
就绪优先级为映像响表
在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---就绪任务列表管理的更多相关文章
- Python3+Selenium3+webdriver学习笔记11(cookie处理)
#!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记11(cookie处理)'''from selenium im ...
- 机器学习实战 - 读书笔记(11) - 使用Apriori算法进行关联分析
前言 最近在看Peter Harrington写的"机器学习实战",这是我的学习心得,这次是第11章 - 使用Apriori算法进行关联分析. 基本概念 关联分析(associat ...
- Ext.Net学习笔记11:Ext.Net GridPanel的用法
Ext.Net学习笔记11:Ext.Net GridPanel的用法 GridPanel是用来显示数据的表格,与ASP.NET中的GridView类似. GridPanel用法 直接看代码: < ...
- SQL反模式学习笔记11 限定列的有效值
目标:限定列的有效值,将一列的有效字段值约束在一个固定的集合中.类似于数据字典. 反模式:在列定义上指定可选值 1. 对某一列定义一个检查约束项,这个约束不允许往列中插入或者更新任何会导致约束失败的值 ...
- uc/os iii移植到STM32F4---IAR开发环境
也许是先入为主的原因,时钟用不惯Keil环境,大多数的教程都是拿keil写的,尝试将官方的uc/os iii 移植到IAR环境. 1.首先尝试从官网上下载的官方移植的代码,编译通过,但是执行会报堆栈溢 ...
- JAVA自学笔记11
JAVA自学笔记11 1:Eclipse的安装 2:用Eclipse写一个HelloWorld案例,最终在控制台输出你的名字 A:创建项目 B:在src目录下创建包.cn.itcast C:在cn.i ...
- 基于μC/OS—III的CC1120驱动程序设计
基于μC/OS—III的CC1120驱动程序设计 时间:2014-01-21 来源:电子设计工程 作者:张绍游,张贻雄,石江宏 关键字:CC1120 嵌入式操作系统 STM32F103ZE ...
- golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好
golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好 jetbrain家的全套ide都很好用,一定要dark背景风格才装B 从File-->s ...
- Spring MVC 学习笔记11 —— 后端返回json格式数据
Spring MVC 学习笔记11 -- 后端返回json格式数据 我们常常听说json数据,首先,什么是json数据,总结起来,有以下几点: 1. JSON的全称是"JavaScript ...
随机推荐
- OLED的波形曲线、进度条、图片显示(STM32 HAL库 模拟SPI通信 5线OLED屏幕)详细篇
少废话,先上效果图 屏幕显示效果 全家福 一.基础认识及引脚介绍 屏幕参数: 尺寸:0.96英寸 分辨率:128*64 驱动芯片:SSD1306 驱动接口协议:SPI 引脚说明: 二. ...
- java.net.NoRouteToHostException: 没有到主机的路由
今天在配置Jenkins 的云服务器的时候提示:java.net.NoRouteToHostException: 没有到主机的路由,网上查到的没有主机路由问题提到的大多是防火墙问题. 查看防火墙状态: ...
- 一键配置 github 可用的 hosts
最近发现访问 Github 各种不畅通, 静态资源经常加载不出来. 写了一个一键脚本修改本机 /etc/hosts 文件, 切换到可用的 IP (数据来自 https://gitee.com/xuew ...
- Manachar’s Algorithm
Manachar's Algorithm Longest palindromic substring - Wikipedia https://en.wikipedia.org/wiki/Longes ...
- Linux下unix socket 读写 抓包
Linux下unix socket 读写 抓包-ubuntuer-ChinaUnix博客 http://blog.chinaunix.net/uid-9950859-id-247877.html
- nginx proxy pass redirects ignore port
nginx proxy pass redirects ignore port $host in this order of precedence: host name from the request ...
- 前序遍历 排序 二叉搜索树 递归函数的数学定义 return 递归函数不能定义为内联函数 f(x0)由f(f(x0))决定
遍历二叉树 traversing binary tree 线索二叉树 threaded binary tree 线索链表 线索化 1. 二叉树3个基本单元组成:根节点.左子树.右子树 以L.D.R ...
- Page (computer memory) Memory segmentation Page table 虚拟地址到物理地址的转换
A page, memory page, or virtual page is a fixed-length contiguous block of virtual memory, described ...
- GDB 简单学习
一般来说,GDB主要帮忙你完成下面四个方面的功能: 1.启动你的程序,可以按照你的自定义的要求随心所欲的运行程序. 2.可让被调试的程序在你所指定的调置的断点处停住.(断点可以是条 ...
- 算法总结篇---KMP算法
目录 写在前面 例题 剪花布条 Radio Transmission OKR-Periods of Words 似乎在梦中见过的样子 Censoring 写在前面 仅为自用,不做推广 一起来看猫片吧! ...