# 时间片的实现学习记录

任务块中关于时间块的功能实现

1 struct os_tcb {
2 CPU_STK *StkPtr;
3 CPU_STK_SIZE StkSize;
4 
5 /* 任务延时周期个数 */
6 OS_TICK TaskDelayTicks;
7 
8 /* 任务优先级 */
9 OS_PRIO Prio;
10 
11 /* 就绪列表双向链表的下一个指针 */
12 OS_TCB *NextPtr;
13 /* 就绪列表双向链表的前一个指针 */
14 OS_TCB *PrevPtr;
15 
16 /*时基列表相关字段*/
17 OS_TCB *TickNextPtr;
18 OS_TCB *TickPrevPtr;
19 OS_TICK_SPOKE *TickSpokePtr;
20 
21 OS_TICK TickCtrMatch;
22 OS_TICK TickRemain;
23 
24 /* 时间片相关字段 */
25 OS_TICK TimeQuanta; (1) 
26 OS_TICK TimeQuantaCtr; (2) 
27 };

/*(1):TimeQuanta 表示任务需要多少个时间片,单位为系统时钟周期
Tick。*/

/*(2):TimeQuantaCtr 表示任务还剩下多少个时间片,每到来一个系统
时钟周期,TimeQuantaCtr 会减一,当 TimeQuantaCtr 等于零的时候,表示时间片用完,任务的 TCB 会从就绪列表链表的头部移动到尾部,好让下一个任务共享时间片。*/


实现时间片调度函数

时间片调度函数 OS_SchedRoundRobin()在 os_core.c 中实现,在 OSTimeTick()(时基处理函数)调用, 具体见代码清单 13-2。在阅读代码清单 13-2 的时候,可配图 13-1 一起理解,该图画的是 在一个就绪链表中,有三个任务就绪,其中在优先级 2 下面有两个任务,均分配了两个时 间片,其中任务 3 的时间片已用完,则位于链表的末尾,任务 2 的时间片还剩一个,则位 于链表的头部。当下一个时钟周期到来的时候,任务 2 的时间片将耗完,相应的 TimeQuantaCtr 会递减为 0,任务 2 的 TCB 会被移动到链表的末尾,任务 3 则被成为链表的 头部,然后重置任务 3 的时间片计数器 TimeQuantaCtr 的值为 2,重新享有时间片。
如圖

/*时间片调度函数*/
1 #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u (1)
2 void OS_SchedRoundRobin(OS_RDY_LIST *p_rdy_list)
3 {
4 OS_TCB *p_tcb;
5 CPU_SR_ALLOC();
6 
7 /* 进入临界段 */
8 CPU_CRITICAL_ENTER();
9 
10 p_tcb = p_rdy_list->HeadPtr; (2)
11 
12 /* 如果 TCB 节点为空,则退出 */
13 if (p_tcb == (OS_TCB *)0) { (3)
14 CPU_CRITICAL_EXIT();
15 return;
16 }
17 
18 /* 如果是空闲任务,也退出 */
19 if (p_tcb == &OSIdleTaskTCB) { (4)
20 CPU_CRITICAL_EXIT();
21 return;
22 }
23 
24 /* 时间片自减 */
25 if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { (5)
26 p_tcb->TimeQuantaCtr--;
27 }
28 
29 /* 时间片没有用完,则退出 */
30 if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { (6)
31 CPU_CRITICAL_EXIT();
32 return;
33 }
34 
35 /* 如果当前优先级只有一个任务,则退出 */
36 if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) { (7)
37 CPU_CRITICAL_EXIT();
38 return;
39 }
40 
41 /* 时间片耗完,将任务放到链表的最后一个节点 */
42 OS_RdyListMoveHeadToTail(p_rdy_list); (8)
43 
44 /* 重新获取任务节点 */
45 p_tcb = p_rdy_list->HeadPtr; (9)
46 /* 重载默认的时间片计数值 */
47 p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; 
48 
49 /* 退出临界段 */
50 CPU_CRITICAL_EXIT();
51 }
52 #endif /* OS_CFG_SCHED_ROUND_ROBIN_EN > 0u */
/*代码清单 13-3 时基周期函数*/
1 void OSTimeTick (void)
2 {
3 /* 更新时基列表 */
4 OS_TickListUpdate();
5 
6 #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u 
7 /* 时间片调度 */ 
8 OS_SchedRoundRobin(&OSRdyList[OSPrioCur]); 
9 #endif 
10 
11 /* 任务调度 */
12 OSSched();
13 }

任务创建函数

任务的时间片在函数创建的时候被指定,具体修改见代码清单 13-4 中的加粗部分。

1 void OSTaskCreate (OS_TCB *p_tcb,
2 OS_TASK_PTR p_task,
3 void *p_arg,
4 OS_PRIO prio,
5 CPU_STK *p_stk_base,
6 CPU_STK_SIZE stk_size,
7 OS_TICK time_quanta, (1)
8 OS_ERR *p_err)
9 {
10 CPU_STK *p_sp;
11 CPU_SR_ALLOC();
12 
13 /* 初始化 TCB 为默认值 */
14 OS_TaskInitTCB(p_tcb);
15 
16 /* 初始化堆栈 */
17 p_sp = OSTaskStkInit( p_task,
18 p_arg,
19 p_stk_base,
20 stk_size );
21 
22 p_tcb->Prio = prio;
23 
24 p_tcb->StkPtr = p_sp;
25 p_tcb->StkSize = stk_size;
26 
27 /* 时间片相关初始化 */
28 p_tcb->TimeQuanta = time_quanta; (2) 
29 #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u 
30 p_tcb->TimeQuantaCtr = time_quanta; (3) 
31 #endif 
32 
33 /* 进入临界段 */
34 OS_CRITICAL_ENTER();
35 
36 /* 将任务添加到就绪列表 */
37 OS_PrioInsert(p_tcb->Prio);
38 OS_RdyListInsertTail(p_tcb);
39 
40 /* 退出临界段 */
41 OS_CRITICAL_EXIT();
42 
43 *p_err = OS_ERR_NONE;
44 }

时间片函数模板

1 int main(void)
2 {
3 OS_ERR err;
4 
5 
6 /* CPU 初始化:1、初始化时间戳 */
7 CPU_Init();
8 
9 /* 关闭中断 */
10 CPU_IntDis();
11 
12 /* 配置 SysTick 10ms 中断一次 */
13 OS_CPU_SysTickInit (10);
14 
15 /* 初始化相关的全局变量 */
16 OSInit(&err);
17 
18 /* 创建任务 */
19 OSTaskCreate( (OS_TCB *)&Task1TCB,
20 (OS_TASK_PTR )Task1,
21 (void *)0,
22 (OS_PRIO )1, (1)
23 (CPU_STK *)&Task1Stk[0],
24 (CPU_STK_SIZE )TASK1_STK_SIZE,
25 (OS_TICK )0, (1)
26 (OS_ERR *)&err );
27 
28 OSTaskCreate( (OS_TCB *)&Task2TCB,
29 (OS_TASK_PTR )Task2,
30 (void *)0,
31 (OS_PRIO )2, (2)
32 (CPU_STK *)&Task2Stk[0],
33 (CPU_STK_SIZE )TASK2_STK_SIZE,
34 (OS_TICK )1, (2)
35 (OS_ERR *)&err );
36 
37 OSTaskCreate( (OS_TCB *)&Task3TCB,
38 (OS_TASK_PTR )Task3,
39 (void *)0,
40 (OS_PRIO )2, (2)
41 (CPU_STK *)&Task3Stk[0],
42 (CPU_STK_SIZE )TASK3_STK_SIZE,
43 (OS_TICK )1, (2)
44 (OS_ERR *)&err );
45 
46 /* 启动 OS,将不再返回 */
47 OSStart(&err);
48 }
49 
50 void Task1( void *p_arg )
51 {
52 for ( ;; ) {
53 flag1 = 1;
54 OSTimeDly(2);
55 flag1 = 0;
56 OSTimeDly(2);
57 }
58 }
59 
60 void Task2( void *p_arg )
61 {
62 for ( ;; ) {
63 flag2 = 1;
64 //OSTimeDly(1); (3)
65 delay(0xff); 
66 flag2 = 0;
67 //OSTimeDly(1); 
68 delay(0xff); 
69 }
70 }
71 
72 void Task3( void *p_arg )
73 {
74 for ( ;; ) {
75 flag3 = 1;
76 //OSTimeDly(1); (3) 
77 delay(0xff); 
78 flag3 = 0;
79 //OSTimeDly(1); 
80 delay(0xff); 
81 }
82 }
/*(1):任务 1 的优先级为 1,时间片为 0。当同一个优先级下有多个任
务的时候才需要时间片功能。
(2):任务 2 和任务 3 的优先级相同,均为 2,且分配相同的时间片,
时间片也可以不同。
(3):因为任务 2 和 3 的优先级相同,分配了相同的时间片,也可以分
配不同的时间片,并把阻塞延时换成软件延时,不管是阻塞延时还是软件延时,延时的时
间都必须小于时间片,因为相同优先级的任务在运行的时候最大不能超过时间片的时间。*/

参考:uC/OS-III 内核实现与应用开发实战指南
—基于野火 STM32 全系列(M3/4/7)开发板

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值