13. 任务的挂起和恢复¶
本章开始,我们让OS的任务支持挂起和恢复的功能,挂起就相当于暂停,暂停后任务从就绪列表中移除,恢复即重新将任务插入到就绪列表。一个任务挂起多少次就要被恢复多少次才能重新运行。
13.1. 实现任务的挂起和恢复¶
13.1.1. 定义任务的状态¶
在任务实现挂起和恢复的时候,要根据任务的状态来操作,任务的状态不同,操作也不同,有关任务状态的宏定义在os.h中实现,总共有9种状态,具体定义见 代码清单14-1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* ---------- 任务的状态 -------*/
#define OS_TASK_STATE_BIT_DLY (OS_STATE)(0x01u)/* /-------- 挂起位 */
/* | */
#define OS_TASK_STATE_BIT_PEND (OS_STATE)(0x02u)/* | /----- 等待位 */
/* | | */
#define OS_TASK_STATE_BIT_SUSPENDED (OS_STATE)(0x04u)/* | | /--- 延时/超时位 */
/* | | | */
/* V V V */
#define OS_TASK_STATE_RDY (OS_STATE)( 0u)/* 0 0 0 就绪 */
#define OS_TASK_STATE_DLY (OS_STATE)( 1u)/* 0 0 1 延时或者超时 */
#define OS_TASK_STATE_PEND (OS_STATE)( 2u)/* 0 1 0 等待 */
#define OS_TASK_STATE_PEND_TIMEOUT (OS_STATE)( 3u)/* 0 1 1 等待+超时*/
#define OS_TASK_STATE_SUSPENDED (OS_STATE)( 4u)/* 1 0 0 挂起 */
#define OS_TASK_STATE_DLY_SUSPENDED (OS_STATE)( 5u)/* 1 0 1 挂起 + 延时或者超时*/
#define OS_TASK_STATE_PEND_SUSPENDED (OS_STATE)( 6u)/* 1 1 0 挂起 + 等待 */
#define OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED (OS_STATE)( 7u)/* 1 1 1 挂起 + 等待 + 超时*/
#define OS_TASK_STATE_DEL (OS_STATE)(255u)
|
13.1.2. 修改任务控制块TCB¶
为了实现任务的挂起和恢复,需要先在任务控制中TCB中添加任务的状态TaskState和任务挂起计数器SusPendCtr这两个成员,具体见 代码清单14-2 的高亮部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | struct os_tcb {
CPU_STK *StkPtr;
CPU_STK_SIZE StkSize;
/* 任务延时周期个数 */
OS_TICK TaskDelayTicks;
/* 任务优先级 */
OS_PRIO Prio;
/* 就绪列表双向链表的下一个指针 */
OS_TCB *NextPtr;
/* 就绪列表双向链表的前一个指针 */
OS_TCB *PrevPtr;
/*时基列表相关字段*/
OS_TCB *TickNextPtr;
OS_TCB *TickPrevPtr;
OS_TICK_SPOKE *TickSpokePtr;
OS_TICK TickCtrMatch;
OS_TICK TickRemain;
/* 时间片相关字段 */
OS_TICK TimeQuanta;
OS_TICK TimeQuantaCtr;
OS_STATE TaskState;(1)
#if OS_CFG_TASK_SUSPEND_EN > 0u(2)
/* 任务挂起函数OSTaskSuspend()计数器 */
OS_NESTING_CTR SuspendCtr;(3)
#endif
};
|
代码清单14-2:(1):TaskState用来表示任务的状态,在本章之前,任务出现了两种状态,一是任务刚刚创建好的时候,处于就绪态,调用阻塞延时函数的时候处于延时态。本章要实现的是任务的挂起态,再往后的章节中还会有等待态,超时态,删除态等。TaskState能够取的值具体见 代码清单14-1。
代码清单14-2:(2):任务挂起功能是可选的,通过宏OS_CFG_TASK_SUSPEND_EN来控制,该宏在os_cfg.h文件中定义。
代码清单14-2:(3):任务挂起计数器,任务每被挂起一次,SuspendCtr递增一次,一个任务挂起多少次就要被恢复多少次才能重新运行。
13.1.3. 编写任务挂起和恢复函数¶
13.1.3.1. OSTaskSuspend()函数¶
OSTaskSuspend()函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | #if OS_CFG_TASK_SUSPEND_EN > 0u
void OSTaskSuspend (OS_TCB *p_tcb,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#if 0/* 屏蔽开始 */(1)
#ifdef OS_SAFETY_CRITICAL
/* 安全检查,OS_SAFETY_CRITICAL_EXCEPTION()函数需要用户自行编写 */
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
/* 不能在ISR程序中调用该函数 */
if (OSIntNestingCtr > (OS_NESTING_CTR)0) {
*p_err = OS_ERR_TASK_SUSPEND_ISR;
return;
}
#endif
/* 不能挂起空闲任务 */
if (p_tcb == &OSIdleTaskTCB) {
*p_err = OS_ERR_TASK_SUSPEND_IDLE;
return;
}
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
/* 不能挂起中断处理任务 */
if (p_tcb == &OSIntQTaskTCB) {
*p_err = OS_ERR_TASK_SUSPEND_INT_HANDLER;
return;
}
#endif
#endif/* 屏蔽结束 */(2)
CPU_CRITICAL_ENTER();
/* 是否挂起自己 */(3)
if (p_tcb == (OS_TCB *)0) {
p_tcb = OSTCBCurPtr;
}
if (p_tcb == OSTCBCurPtr) {
/* 如果调度器锁住则不能挂起自己 */
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SCHED_LOCKED;
return;
}
}
*p_err = OS_ERR_NONE;
/* 根据任务的状态来决定挂起的动作 */(4)
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY:(5)
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
p_tcb->SuspendCtr = (OS_NESTING_CTR)1;
OS_RdyListRemove(p_tcb);
OS_CRITICAL_EXIT_NO_SCHED();
break;
case OS_TASK_STATE_DLY:(6)
p_tcb->TaskState = OS_TASK_STATE_DLY_SUSPENDED;
p_tcb->SuspendCtr = (OS_NESTING_CTR)1;
CPU_CRITICAL_EXIT();
break;
case OS_TASK_STATE_PEND:(7)
p_tcb->TaskState = OS_TASK_STATE_PEND_SUSPENDED;
p_tcb->SuspendCtr = (OS_NESTING_CTR)1;
CPU_CRITICAL_EXIT();
break;
case OS_TASK_STATE_PEND_TIMEOUT:(8)
p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED;
p_tcb->SuspendCtr = (OS_NESTING_CTR)1;
CPU_CRITICAL_EXIT();
break;
case OS_TASK_STATE_SUSPENDED:(9)
case OS_TASK_STATE_DLY_SUSPENDED:
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
p_tcb->SuspendCtr++;
CPU_CRITICAL_EXIT();
break;
default:(10)
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_STATE_INVALID;
return;
}
/* 任务切换 */
OSSched();(11)
}
#endif
|
代码清单14-3:(1)和(2):这部分代码是为了程序的健壮性写的代码,即是加了各种判断,避免用户的误操作。在uC/OS- III中,这段代码随处可见,但为了讲解方便,我们把这部分代码注释掉,里面涉及到的一些宏和函数我们均不实现,只需要了解即可,在后面的讲解中,要是出现这段代码,我们直接删除掉,删除掉也不会影响核心功能。
代码清单14-3:(3):如果任务挂起的是自己,则判断下调度器是否锁住,如果锁住则退出返回错误码,没有锁则继续往下执行。
代码清单14-3:(4):根据任务的状态来决定挂起操作。
代码清单14-3:(5):任务在就绪状态,则将任务的状态改为挂起态,挂起计数器置1,然后从就绪列表删除。
代码清单14-3:(6):任务在延时状态,则将任务的状态改为延时加挂起态,挂起计数器置1,不用改变TCB的位置,即还是在延时的时基列表。
代码清单14-3:(7):任务在等待状态,则将任务的状态改为等待加挂起态,挂起计数器置1,不用改变TCB的位置,即还是在等待列表等待。等待列表暂时还没有实现,将会在后面的章节实现。
代码清单14-3:(8):任务在等待加超时态,则将任务的状态改为等待加超时加挂起态,挂起计数器置1,不用改变TCB的位置,即还在等待和时基这两个列表中。
代码清单14-3:(9):只要有一个是挂起状态,则将挂起计数器加一操作,不用改变TCB的位置。
代码清单14-3:(10):其它状态则无效,退出返回状态无效错误码。
代码清单14-3:(11):任务切换。凡是涉及到改变任务状态的地方,都需要进行任务切换。
13.1.3.2. OSTaskResume()函数¶
OSTaskResume()函数用于恢复被挂起的函数,但是不能恢复自己,挂起倒是可以挂起自己,具体实现见 代码清单14-4。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | #if OS_CFG_TASK_SUSPEND_EN > 0u
void OSTaskResume (OS_TCB *p_tcb,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#if 0/* 屏蔽开始 */(1)
#ifdef OS_SAFETY_CRITICAL
/* 安全检查,OS_SAFETY_CRITICAL_EXCEPTION()函数需要用户自行编写 */
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
/* 不能在ISR程序中调用该函数 */
if (OSIntNestingCtr > (OS_NESTING_CTR)0) {
*p_err = OS_ERR_TASK_RESUME_ISR;
return;
}
#endif
CPU_CRITICAL_ENTER();
#if OS_CFG_ARG_CHK_EN > 0u
/* 不能自己恢复自己 */
if ((p_tcb == (OS_TCB *)0) ||
(p_tcb == OSTCBCurPtr)) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_RESUME_SELF;
return;
}
#endif
#endif/* 屏蔽结束 */(2)
*p_err = OS_ERR_NONE;
/* 根据任务的状态来决定挂起的动作 */
switch (p_tcb->TaskState) {(3)
case OS_TASK_STATE_RDY:(4)
case OS_TASK_STATE_DLY:
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_SUSPENDED;
break;
case OS_TASK_STATE_SUSPENDED:(5)
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
p_tcb->SuspendCtr--;
if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0) {
p_tcb->TaskState = OS_TASK_STATE_RDY;
OS_TaskRdy(p_tcb);
}
OS_CRITICAL_EXIT_NO_SCHED();
break;
case OS_TASK_STATE_DLY_SUSPENDED:(6)
p_tcb->SuspendCtr--;
if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0) {
p_tcb->TaskState = OS_TASK_STATE_DLY;
}
CPU_CRITICAL_EXIT();
break;
case OS_TASK_STATE_PEND_SUSPENDED:(7)
p_tcb->SuspendCtr--;
if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0) {
p_tcb->TaskState = OS_TASK_STATE_PEND;
}
CPU_CRITICAL_EXIT();
break;
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:(8)
p_tcb->SuspendCtr--;
if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0) {
p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT;
}
CPU_CRITICAL_EXIT();
break;
default:(9)
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_STATE_INVALID;
return;
}
/* 任务切换 */
OSSched();(10)
}
#endif
|
代码清单14-4:(1)和(2):这部分代码是为了程序的健壮性写的代码,即是加了各种判断,避免用户的误操作。在uC/OS- III中,这段代码随处可见,但为了讲解方便,我们把这部分代码注释掉,里面涉及到的一些宏和函数我们均不实现,只需要了解即可,在后面的讲解中,要是出现这段代码,我们直接删除掉,删除掉也不会影响核心功能。
代码清单14-4:(3):根据任务的状态来决定恢复操作。
代码清单14-4:(4):只要任务没有被挂起,则退出返回任务没有被挂起的错误码。
代码清单14-4:(5):任务只在挂起态,则递减挂起计数器SuspendCtr,如果SuspendCtr等于0,则将任务的状态改为就绪态,并让任务就绪。
代码清单14-4:(6):任务在延时加挂起态,则递减挂起计数器SuspendCtr,如果SuspendCtr等于0,则将任务的状态改为延时态。
代码清单14-4:(7):任务在延时加等待态,则递减挂起计数器SuspendCtr,如果SuspendCtr等于0,则将任务的状态改为等待态。
代码清单14-4:(8):任务在等待加超时加挂起态,则递减挂起计数器SuspendCtr,如果SuspendCtr等于0,则将任务的状态改为等待加超时态
代码清单14-4:(9):其它状态则无效,退出返回状态无效错误码。
代码清单14-4:(10):任务切换。凡是涉及到改变任务状态的地方,都需要进行任务切换。
13.2. main函数¶
这里,我们创建任务1、2和3,其中任务的优先级为1,任务2的优先级为2,任务3的优先级为3。任务1将自身的flag每翻转一次后均将自己挂起,任务2在经过两个时钟周期后将任务1恢复,任务3每隔一个时钟周期反转一次。具体代码见 代码清单14-5。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | int main(void)
{
OS_ERR err;
/* CPU初始化:1、初始化时间戳 */
CPU_Init();
/* 关闭中断 */
CPU_IntDis();
/* 配置SysTick 10ms 中断一次 */
OS_CPU_SysTickInit (10);
/* 初始化相关的全局变量 */
OSInit(&err);
/* 创建任务 */
OSTaskCreate( (OS_TCB *)&Task1TCB,
(OS_TASK_PTR )Task1,
(void *)0,
(OS_PRIO )1,
(CPU_STK *)&Task1Stk[0],
(CPU_STK_SIZE )TASK1_STK_SIZE,
(OS_TICK )0,
(OS_ERR *)&err );
OSTaskCreate( (OS_TCB *)&Task2TCB,
(OS_TASK_PTR )Task2,
(void *)0,
(OS_PRIO )2,
(CPU_STK *)&Task2Stk[0],
(CPU_STK_SIZE )TASK2_STK_SIZE,
(OS_TICK )0,
(OS_ERR *)&err );
OSTaskCreate( (OS_TCB *)&Task3TCB,
(OS_TASK_PTR )Task3,
(void *)0,
(OS_PRIO )3,
(CPU_STK *)&Task3Stk[0],
(CPU_STK_SIZE )TASK3_STK_SIZE,
(OS_TICK )0,
(OS_ERR *)&err );
/* 启动OS,将不再返回 */
OSStart(&err);
}
void Task1( void *p_arg )
{
OS_ERR err;
for ( ;; ) {
flag1 = 1;
OSTaskSuspend(&Task1TCB,&err);
flag1 = 0;
OSTaskSuspend(&Task1TCB,&err);
}
}
void Task2( void *p_arg )
{
OS_ERR err;
for ( ;; ) {
flag2 = 1;
OSTimeDly(1);
//OSTaskResume(&Task1TCB,&err);
flag2 = 0;
OSTimeDly(1);;
OSTaskResume(&Task1TCB,&err);
}
}
void Task3( void *p_arg )
{
for ( ;; ) {
flag3 = 1;
OSTimeDly(1);
flag3 = 0;
OSTimeDly(1);
}
}
|