μC/OSIII学习day5
viys 2022-12-06 μC/OS
介绍
时间片,任务的挂起和恢复
# 时间片
# μC/OS如何实现时间片
# 任务TCB
struct os_tcb{
...
/* 时间片相关字段 */
OS_TICK TimeQuanta;
OS_TICK TimeQuantaCtr;
...
}
1
2
3
4
5
6
7
2
3
4
5
6
7
TimeQuanta
表示任务需要多少个时间片,单位为系统时钟周期Tick.TimeQuantaCtr
表示任务还剩下多少个时间,每到来一个系统时钟周期TimeQuantaCtr
会减一,当TimeQuantaCtr
等于0的时候,表示时间片用完,任务的TCB会从就绪列表链表的头部移动到尾部,好让下一个任务共享时间片.
# 时间片调度函数
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
void OS_SchedRoundRobin(OS_RDY_LIST *p_rdy_list){
OS_TCB *p_tcb;
CPU_SR_ALLOC();
/* 进入临界段 */
CPU_CRITICAL_ENTER();
p_tcb = p_rdy_list->HeadPtr;
/* 如果 TCB 节点为空,则退出 */
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) {
CPU_CRITICAL_EXIT();
return;
}
/* 如果当前优先级只有一个任务,则退出 */
if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) {
CPU_CRITICAL_EXIT();
return;
}
/* 时间片耗完,将任务放到链表的最后一个节点 */
OS_RdyListMoveHeadToTail(p_rdy_list);
/* 重新获取任务节点 */
p_tcb = p_rdy_list->HeadPtr;
/* 重载默认的时间片计数值 */
p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta;
/* 退出临界段 */
CPU_CRITICAL_EXIT();
}
#endif/* OS_CFG_SCHED_ROUND_ROBIN_EN > 0u */
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
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
- 时 间 片 是 一 个 可 选 的 功 能, 是否选择由
OS_CFG_SCHED_ROUND_ROBIN_EN
控制,该宏在os_cfg.h定义. - 时间片用完,如果该优先级下有两个以上任务,则将刚刚消耗完时间片的节点移到链表的末尾,此时位于末尾的任务的TCB字段中的
TimeQuantaCtr
是等于0的,只有等他下一次运行的时候值才会重置为TimeQuanta
.
# OSTimeTick()函数
任务的时间片的单位在每个系统时钟周期到来的时候被更新,时间片调度函数则由时基周期处理函数OSTimeTick()
调用.
void OSTimeTick(void){
/* 更新时基列表 */
OS_TickListUpdate();
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
/* 时间片调度 */
OS_SchedRoundRobin(&OSRdyList[OSPrioCur]);
#endif
/* 任务调度 */
OSSched();
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# OSTaskCreate()函数
任务的时间片在函数创建的时候被指定.
void OSTaskCreate (OS_TCB *p_tcb,
OS_TASK_PTR p_task,
void *p_arg,
OS_PRIO prio,
CPU_STK *p_stk_base,
CPU_STK_SIZE stk_size,
OS_TICK time_quanta,
OS_ERR *p_err) {
CPU_STK *p_sp;
CPU_SR_ALLOC();
/* 初始化 TCB 为默认值 */
OS_TaskInitTCB(p_tcb);
/* 初始化栈 */
p_sp = OSTaskStkInit( p_task,
p_arg,
p_stk_base,
stk_size );
p_tcb->Prio = prio;
p_tcb->StkPtr = p_sp;
p_tcb->StkSize = stk_size;
/* 时间片相关初始化 */
p_tcb->TimeQuanta = time_quanta;
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
p_tcb->TimeQuantaCtr = time_quanta;
#endif
/* 进入临界段 */
OS_CRITICAL_ENTER();
/* 将任务添加到就绪列表 */
OS_PrioInsert(p_tcb->Prio);
OS_RdyListInsertTail(p_tcb);
/* 退出临界段 */
OS_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
}
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
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
- 初始化时间片计数器
TimeQuantaCtr
的值等于TimeQuanta
,每经过一个系统时钟周期,该值会递减,如果该值为0,则表示时间片耗完.
# OS_IdleTaskInit() 函数
void OS_IdleTaskInit(OS_ERR *p_err){
/* 初始化空闲任务计数器 */
OSIdleTaskCtr = (OS_IDLE_CTR)0;
/* 创建空闲任务 */
OSTaskCreate( (OS_TCB *)&OSIdleTaskTCB,
(OS_TASK_PTR )OS_IdleTask,
(void *)0,
(OS_PRIO)(OS_CFG_PRIO_MAX - 1u),
(CPU_STK *)OSCfg_IdleTaskStkBasePtr,
(CPU_STK_SIZE)OSCfg_IdleTaskStkSize,
(OS_TICK )0,
(OS_ERR *)p_err );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 任务的挂起和恢复
# μC/OS如何实现任务的挂起和恢复
# 任务的状态
任务实现挂起和恢复的时候,根据任务的状态来操作,任务的状态不同,操作也不同.
os.h
/* ---------- 任务的状态 -------*/
#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)/* 延时/超时位 */
#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)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 任务控制块TCB
struct os_tcb{
...
OS_STATE TaskState;
#if OS_CFG_TASK_SUSPEND_EN > 0u
/* 任务挂起函数 OSTaskSuspend() 计数器 */
OS_NESTING_CTR SuspendCtr;
#endif
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 任务挂起计数器,任务每被挂起一次,
SuspendCtr
递增一次,一个任务挂起多少次就要被恢复多少次才能重新运行.
# 任务挂起和恢复函数
OSTaskSuspend()
函数//任务挂起函数 #if OS_CFG_TASK_SUSPEND_EN > 0u void OSTaskSuspend(OS_TCB *p_tcb,OS_ERR *p_err){ CPU_SR_ALLOC(); #if 0/* 屏蔽开始 */ #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/* 屏蔽结束 */ CPU_CRITICAL_ENTER(); /* 是否挂起自己 */ 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; /* 根据任务的状态来决定挂起的动作 */ switch (p_tcb->TaskState) { case OS_TASK_STATE_RDY: 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: p_tcb->TaskState = OS_TASK_STATE_DLY_SUSPENDED; p_tcb->SuspendCtr = (OS_NESTING_CTR)1; CPU_CRITICAL_EXIT(); break; case OS_TASK_STATE_PEND: 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: 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: 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: CPU_CRITICAL_EXIT(); *p_err = OS_ERR_STATE_INVALID; return; } /* 任务切换 */ OSSched(); } #endif
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- 这部分代码是为了程序的健壮性写的代码,加了各种判断,避免用户的误操作.
OSTaskResume()函数
//恢复被挂起的函数(除了自己) #if OS_CFG_TASK_SUSPEND_EN > 0u void OSTaskResume(OS_TCB *p_tcb,OS_ERR *p_err){ CPU_SR_ALLOC(); #if 0/* 屏蔽开始 */ #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/* 屏蔽结束 */ *p_err = OS_ERR_NONE; /* 根据任务的状态来决定挂起的动作 */ switch (p_tcb->TaskState) { case OS_TASK_STATE_RDY: 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: 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: 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: 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: p_tcb->SuspendCtr--; if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0) { p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT; } CPU_CRITICAL_EXIT(); break; default: CPU_CRITICAL_EXIT(); *p_err = OS_ERR_STATE_INVALID; return; } /* 任务切换 */ OSSched(); } #endif
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- 这部分代码是为了程序的健壮性写的代码,加了各种判断,避免用户的误操作.