简易内核调度设计--基于TQ2440(ARM9)的实现(2)

作者:姜广伟
日期:2010-12-24
邮箱:Guangwei.jiang@gmail.com/jgw2008@126.com

**

1. 引言

**
上一个练习中(TaskSwitch),我们通过“Task_Sw_1To2”和“Task_Sw_2To1”实现了“任务1”和“任务2”之间的切换。有没有办法,用统一的函数“Task_Sw”实现不同任务间的切换?
答案是肯定的。如何实现呢?
本章就带领大家一探究竟。

首先,还是先看看这个程序(TaskSwitchEx)的功能:
Task1中,闪烁LED1灯3次后,主动将CPU使用权切换给其他任务;
Task2中,闪烁LED2灯3次后,主动将CPU使用权切换给其他任务。
至于这个“其他任务”是谁,就不是Task1/Task2所能控制的了。

实验环境说明:
本实验的操作平台为TQ2440(ARM9)开发板,程序代码在S3C2440“SteppingStone”的4KB内部RAM中运行。程序就是利用这4KB的空间来实现多任务的切换。
请选择从NAND Flash启动,并将生成的二进制文件烧到NAND Flash中。
当选择从NAND Flash启动CPU时,CPU会通过内部硬件将NAND Flash开始的4KB字节数据复制到“SteppingStone” 的4KB内部RAM中(此时内部RAM的起始地址是0),然后跳到地址0开始执行。

下面,还是先看看Task1和Task2的代码,

这里写图片描述

这里写图片描述

2. 详解设计/实现部分

程序分为3大部分,
–初始化
–存储模型
–任务切换

2.1 初始化

程序的初始化很简单,_start是CPU reset后运行的第一段代码。
a. 因为要调用C函数,必须要先设置了SVC模式下的堆栈(CPU reset后,默认进入SVC模式);
b. 为了防止CPU不断自动重启,需要关闭WatchDog;
c. 接下来初始化4个LED灯(关闭4个LED灯);
d. 初始化”下一个任务”的指向“任务1”, 关于调度算法,后面会讲到;
e. 最后,启动第一个任务Task1。
注意:Task1堆栈的初始化指针指向#4000,它工作在SVC模式中。

初始化代码(_start)如下,

这里写图片描述

2.2存储模型

Code存放区域: 0x00000000~0x000007FF (2KB);
Task2的堆栈区域: 0x00000C00~0x00000E03(516B);
Task1的堆栈区域: 0x00000E04~0x00000FA3(416B);
TCB指针存放区域: 0x00000FA4~0x00000FFF(92B)。

注意:
a. 栈的增长方向由高地址到低地址;
b. TCB指针存放区域,存放的是Task栈顶指针,如0x0000FF4保存Task1的栈顶指针;0x00000FF0保存Task2的栈顶指针。

存储划分图表如下,

这里写图片描述

2.3任务切换

运行新任务之前,必须要为每个新任务初始化PCB(Process Control Block)。PCB是一个保留在RAM的数据结构,用于保存所有ARM寄存器的一个副本(见下表)。

任务控制块(PCB, Process Control Block)

这里写图片描述

测试代码中,任务1是主程序并首先运行,不需要初始化PCB。
任务2运行之前,需要先初始化PCB,初始化代码如下,

这里写图片描述

其中,函数“TaskCreate”的3个参数含义如下:
void(*Task)(void): Task地址
unsigned long *p_Stack: Task堆栈指针
unsigned long *Task_STK_PTR: 保存Task栈顶指针的地址

首先,解释任务调度的原则。
我们采用最简单的“轮转法调度”,即Task1->Task2->Task3->…
在运行第一个任务之前,要先初始化“下一个任务”,即指向“任务1”;
任务切换过程中,要计算出“当前任务”和“下一个任务”的指向:
“当前任务”就是上一次调度之前的“下一个任务”;
“下一个任务”就是“当前任务”的id加1,如“当前任务”是“任务1”,则“下一个任务”是“任务2”。如果“下一个任务”的id大于任务总数(我们也可称之为“越界”),则“下一个任务”指向第一个任务。

接着,介绍两个变量的作用。
TaskCur_STK_PTR:保存“当前任务”堆栈指针的地址;
TaskNext_STK_PTR:保存“下一个任务”堆栈指针的地址。

然后,介绍“任务x”堆栈指针保存的地址。
在我们的练习中,为了简化难道,任务x”堆栈指针是保存在连续的地址空间中。
如,保存Task1堆栈指针的地址是0x00000FF4; 保存Task2堆栈指针的地址是0x00000FF0。

下面是具体实现,
第一阶段,保存“当前任务”运行的上下文(Context)。
a. 首先,将“当前任务”寄存器值压栈;
b. 接着,计算存放“当前任务”堆栈指针的地址;
c. 最后,保存“当前任务”的栈顶指针。

这里写图片描述

第二阶段,恢复“下一个任务”的运行上下文(Context)。
a. 首先,计算“下一个任务”堆栈指针;
b. 接着,取出“下一个任务”的栈顶指针;
c. 最后,将寄存器依次出栈。

这里写图片描述

本文源代码可以从作者的github下载,链接如下
https://github.com/GuangweiJiang/diy_kernel/tree/master/02_TaskSwitchEx

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值