使用最简单的操作,最低的效率,最笨的方法,去做出一个像是操作系统一样的东西
然后在一步一步去改善性能,完善功能
我是一名嵌入式软件开发者,从很早之前上学开始就已经了解过一些rtos的操作系统,但是因为种种原因没能深入学习下去,现在重新出发,通过已经积累的知识,参考已有的RTOS等等系统,从一个小白的思考一步步做下去
本节主题:嵌入式操作系统的任务与任务切换
一、前言
在上一篇文章中,零零散散介绍了操作系统为什么要以任务来划分,操作系统与裸机运行的区别以及带来的优势。现在重新再回回顾一下。
-
操作系统本身就是围观上串行执行,宏观上并行执行
-
通过划分不同的任务,提供更加细微的控制粒度
-
裸机运行时任何一个任务阻塞会造成处理器空转浪费,除此之外,其他的任务得不到执行,也造成性能比较低
-
操作系统可以有效利用各个任务的无效阻塞时间切换执行,高效的利用处理器
我们的第一个目标就是实现一个多任务功能,可以创建多个任务,处理器可以按照一定的策略在各个任务之间随意切换。
二、操作系统切换任务要做什么
我们要实现多个任务以及任务切换,那么这里就出现了2个问题需要去解决
-
任务 静态的定义
-
切换 动态的过程
在上一节中我们已经给出了任务的定义,非常简单
struct task
{
void *pc;
void *sp;
unsigned long regs[32];
};
再去啰嗦一下,我们再来分析一下一个任务到底有什么东西
/**
* @brief taska
*
*/
struct task taska;
char taska_stack[4096];
void taska_fun(void)
{
while (1)
{
printf("i am taska run!\n");
mdelay(1000);
}
}
/**
* @brief taskb
*
*/
struct task taskb;
char taskb_stack[4096];
void taskb_fun(void)
{
while (1)
{
printf("i am taskb run!\n");
mdelay(1000);
}
}
上面定义了2个任务,taska和taskb,每个任务的所有内容,可以分成以下几个部分:
-
定义任务状态结构体
-
定义一个全局变量,作为任务的堆栈
-
定义任务的函数体
-
任务信息结构体
结构体用于存放任务的一些私有信息,在切换到其他任务时候需要把任务的私有信息保存到这里来,在从其他任务切换过来的时候,需要从这里把任务的私有状态信息恢复
-
全局变量--堆栈
堆栈其实没有那么神秘,只要是一个可读可写的内存,就可以作为栈。在操作系统中,每一个任务使用属于自己的堆栈,说以要为每一个任务划分一个栈空间
-
任务的函数体
函数体,就是任务的代码,这个部分在任务执行和切换的过程中是不会改变,所以我们不需要特别的处理
操作系统 | 生活租房 | |
---|---|---|
处理器 | 租来的房子 | |
任务 | 租房者 | |
任务结构体信息 | 租房者的个人物品 |
我为什么要这样做比较呢,让我们思考下任务切换与租房搬家的场景:
时间 | 操作系统 | 生活租房 | |
---|---|---|---|
T1 | 操作系统未创建任务 | 有一间房子是空置的,还没有人住在里面 | |
T2 | 操作系统创建了一个任务A | 有一个人要租房子 | |
T3 | 处理器当前空闲,可以执行该任务A | 租客A找到了上面空置的房子 | |
T4 | 处理器执行任务A | 租客A要到房子里面去住 | |
T5 | 只有代码没法执行,任务A还有一些私有的寄存器状态 | 只有租客A搬到房子里面无法生活,需要携带各种私人物品 | |
T6 | 把任务A的结构体内容恢复到处理器,也就是把一些通用寄存器恢复,实现的过程就是把任务结构体保存的寄存器内容搬到处理器 | 把租客B的各种私有物品也搬到出租房间里面来 | 此时无论是房间还是处理器,都任务是空的,所以我们搬进了住进来都不需要理会之前的状态,可以直接覆盖原来的内容,对应一个apitask_switch_to |
T7 | 因为一些原因,操作系统要换到另一个任务B执行 | 因为一些原因,房东要换另一个人租客B住进来 | |
T8 | 任务A准备离开退出处理器 | 租客A准备离开退出房间 | |
T9 | 任务A离开的时候,把自己此时的状态保存到任务状态结构体中,主要就是一些通用寄存器 | 租客A离开的时候,屋里之前也买了很多贵重的东西,当然也要带走 | |
T10 | 任务B准备进入处理器 | 租客B准备进入到房间 | |
T11 | 任务B将自己保存在状态信息结构体中的内容恢复到处理器 | 租客B把字节的私人物品,如洗漱用品、衣服等等搬到房间里 | |
T12 | 任务B开始在处理器运行 | 租客B开始在房间里生活 | |
T13 | 过了一段时间,操作系统决定要让任务B离开处理器,让任务A进来执行 | 过了一段时间,房东通知租客B要离开房间,要让租客A住进来 | |
T14 | 任务B把自己的状态保存到结构体 | 租客B不得不把自己的东西从房间里搬出去 | 如果不搬走的话,东西会丢 |
T15 | 任务A把自己任务状态恢复到处理器 | 租客A把自己的物品搬到房间里 | |
T16 | 任务A开始再次在处理器执行 | 租客A再次住到了房间里 | |
... | ... | ||
上面啰嗦了一大部分描述了任务在处理器的切换与房间里不同的租客的切换,总结出来相似的要点就是:
-
任务和租客都有自己的私人物品
-
任务被执行时,要把自己的私有状态搬到处理器
-
租客租到房子时,要把自己的私人物品搬到房间里
-
任务被切换掉时,要把处理器此时的状态保存到自己的状态结构体
-
租客离开房子时,要把自己的东西全部搬走
三、定义任务切换的API
根据上面对任务切换场景的模拟,我们暂时先定义2个API,后面根据需要再去补充
-
当前处理器无任务时,切换到第一个任务
task_switch_to(struct task *to)
-
当前处理器正在执行一个任务,切换到另一个任务
task_switch_from_to(struct task *from, struct task *to)
根据上面对任务切换的模拟和理解,上面的API要做什么工作应该心里有了一些想法
void task_switch_to(struct task *to)
{
//1. 把自己在结构体中的状态恢复到处理器
//2. 开始执行任务to
}
void task_switch_from_to(struct task *from, struct task *to)
{
//1. 把当前处理器的状态保存到from任务
//2. 把任务to的状态恢复到处理器
//3. 开始执行任务to
}
上面具体的实现将会在github项目代码中提供
https://github.com/jhbdream/armv8_os
四、任务切换
任务切换需要一定的策略和一定的时机,还需要一个上帝视角,也就是操作系统
当我们完成了上面的2个API之后,就可以进行动态的自发的任务切换
本节先完成上面的接口功能,下一节处理定时中断与任务切换
如果大家需要有相关交流沟通咨询,可以在后台回复消息