8.11和Linux相似的任务切换方法
有些人认为TSS切换任务太慢了,因为TSS切换的过程中硬件要做太多操作了
详细可以看Intel手册硬件完成了哪些操作
所以我们手动保存这些寄存器信息
我们采用的就是将其保存在任务自己的栈中
一个函数调用另一个函数,有些寄存器是可以不用保存的,有些要自己保存
详细要看Intel手册abi386-4第37页
EIP当前代码的位置,就是那个返回地址
esp栈顶指针,esp要指向栈顶,所以无法通过栈来保存此时的栈顶指针,所以保存在task_t结构体里面
typedef struct _task_t {
uint32_t * stack;
tss_t tss; // 任务的TSS段
uint16_t tss_sel; // tss选择子
}task_t;
因为涉及栈的操作,所以需要汇编代码来写
//task.c
void simple_switch (uint32_t ** from, uint32_t * to);
void task_switch_from_to (task_t * from, task_t * to) {
// switch_to_tss(to->tss_sel);
simple_switch(&from->stack, to->stack);
}
//D:\code\x86\code\start\start\source\kernel\init\start.S
// simple_switch(&from,&to)
.text
.global simple_switch
simple_switch: //有个问题就是不用保存void task_switch_from_to (task_t * from, task_t * to)的返回IP吗?所以我感觉下面两个要多移4个字节
movl 4(%esp), %eax // esp往栈底移动4个字节(一个单位),然后取出其内容,然后取from->stack
movl 8(%esp), %edx // esp往栈底移动8个字节(两个单位),然后取出其内容,取to->stack
// 保存前一任务(也可以说为本任务)状态
push %ebp
push %ebx
push %esi
push %edi
// 切换栈
mov %esp, (%eax) // 将from->stack = esp
mov %edx, %esp // esp = to->stack
// 加载下一任务的栈(但是一开始init_task_entry(void)函数的栈是空的,所以需要我们手动给栈赋初值)
pop %edi
pop %esi
pop %ebx
pop %ebp
ret
//D:\code\x86\code\start\start\source\kernel\core\task.c
int task_init (task_t *task, uint32_t entry, uint32_t esp) { //init_task_entry(void)的esp为一个初始化的数组
ASSERT(task != (task_t *)0);
// tss_init(task, entry, esp);
uint32_t * pesp = (uint32_t *)esp;
if (pesp) {
*(--pesp) = entry; //任务函数的入口地址(相当于EIP)在栈底,给ret用
*(--pesp) = 0;
*(--pesp) = 0;
*(--pesp) = 0;
*(--pesp) = 0;
task->stack = pesp; //这是init_task_entry(void)函数的esp
}
return 0;
}