从编写操作系统的角度看上下文切换

0 上下文切换

上下文切换(Context Switching)指的是切换处理器执行的任务时发生的一系列动作。

感性地说,从一个任务切换到另一个任务,过一段时间后又想切换回来,必然是需要保存一些东西的,这些东西就是某个任务的上下文。

因此,被要实现多任务的操作系统,或者简单的任务调度器,就肯定绕不过上下文切换。

而要知道上下文切换到底需要做什么,可以想看看描述一个任务及其状态到底需要哪些参数。

1 任务创建

先看看任务创建需要的所有信息——地址寄存器、一些特殊寄存器和返回地址。而所谓的上下文(context,避免读着别扭后面一直用context代上下文),也在介绍中断时作了定义啦。

对操作系统而言,创建任务等同于初始化用户提供的栈和入口地址(entry point)的上下文(context)。通常创建任务的函数都是没有返回值的。创建任务需要的参数包括入口地址、栈地址、栈大小和传给栈的参数。原型类似于下面这样。

void add_task(taskFunc *entry, void *stack, unsigned int stack_size, void *arg);

这个函数拿到这些参数信息之后,需要做两件事情——初始化任务堆栈、向操作系统或者运行时(runtime)注册任务,也就是告知系统该任务的信息。

2 注册任务

一般说来,操作系统或者系统内核会用一个叫任务控制块(Task Control Block,TCB)的数据结构来保存任务的信息。

对于简单的运行时系统而言,不需要保存详细的任务信息,只需要保存各任务堆栈的入口地址task_stacks[i]、已经注册的任务数量allocated_tasks,以及当前任务的标号current_task(也可以是别的,主要是用来标记处理器现在在执行的的哪一个任务,即task_stacks[current_task]。所以维护下面这几个全局变量就绰绰有余了。

unsigned int *task_stacks[MAXTASKS];
unsigned int allocated_tasks = 0;
int current_task = -1;

3 初始化任务栈

任务栈的初始化也是分两步走——第一步是计算任务栈栈顶(the top of the stack)地址,第二步是填充任务栈。

略有奇怪哈,要得到任务栈栈顶地址的话,直接用任务栈地址+栈长度不就好啦,怎么感觉还需要别的操作,单独成一步?答案是栈顶地址有字节对齐需求。

任务创建函数往往不会对堆栈地址和长度设置约束,但处理器可能需要堆栈的地址是 1632 或者 64 的倍数,这就需要咱在计算栈顶地址时做一点处理。

sp = (unsigned int *)((int)(stack + stack_size) & 0xfffffff0);

像这样,把地址末尾不足 f 的值抹去,也就是对求和结果做一下向下取整,这样就能保证栈顶地址(sp)是16的倍数。

这里又联想到内存管理。内存管理算法也会刻意对申请地空间大小做这样的向下取整操作,以减少内存中的小碎片。这样横向比较一下,也就能理解malloc函数返回实际分配的空间大小的意义了

参考

  • Xtensa, programmers_guide.pdf, chapter 8 Context Switching
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值