手写操作系统项目----进程切换
讲解了项目中是如何进程进程切换的。\
项目地址:https://github.com/xjintong/simpleOS
一、宏观进程切换
在
的结构,源码如下:task.h
中定义了一个
task_manager_t
typedef struct _task_manager_t {
task_t * curr_task; // 当前运行的任务
list_t ready_list; // 就绪队列
list_t task_list; // 保存所有已经创建好的进程 所有已创建任务的队列
list_t sleep_list; // 睡眠队列 延时队列
task_t first_task; // 内核任务
task_t idle_task; // 空闲任务
int app_code_sel; // 任务代码段选择子
int app_data_sel; // 应用任务的数据段选择子
}task_manager_t;
这里定义了当前运行的任务,就绪队列、睡眠队列等等,进行任务切换的时候,要操作这个结构。\
进程切换的具体源码如下:
int sys_sched_yield() {
// 进入中断保护--保存当前寄存器状态,然后关闭中断。
irq_state_t state = irq_enter_protection();
if (list_count(&task_manager.ready_list) > 1) {
task_t * curr_task = task_current();
// 如果队列中还有其它任务,则将当前任务移入到队列尾部
task_set_block(curr_task);
task_set_ready(curr_task); // 再加入的时候,是加入队列的尾部
// 切换至下一个任务,在切换完成前要保护,不然可能下一任务
// 由于某些原因运行后阻塞或删除,再回到这里切换将发生问题
task_dispatch();
}
// 退出中断保护,恢复中断前的寄存器的值
irq_leave_protection(state);
// 如果就绪队列里面就1个进程。
return 0;
}
- 先将中断关闭
(防止进程切换过程中,被中断打断造成一些错误)。irq_enter_protection()
- 然后判断当前就绪队列中有多少个任务,如果只有这个任务就不切换了;如果有多个任务,就先将当前任务放到就绪队列的尾部(先将任务从就绪队列中移除--
)。task_set_block(curr_task);
,然后将任务放到就绪队列尾部--
task_set_ready(curr_task);
- 最后切换到下一个进程。
二、细扣切换的函数
task_dispatch();源码如下:
void task_dispatch (void) {
irq_state_t state = irq_enter_protection();
task_t * to = task_next_run();
if (to != task_manager.curr_task) {
task_t * from = task_manager.curr_task;
task_manager.curr_task = to;
to->state = TASK_RUNNING;
task_switch_from_to(from, to);
}
irq_leave_protection(state);
}
- 进入中断保护
- 从就绪队列中取出下一个任务。(
)task_t * to = task_next_run();
- 通过
)task_manager_t
结构体获取当前任务(
from = task_manager.curr_task;
- 将
中当前运行的任务改为to任务。task_manager_t
- 将to任务的状态改为TASK_RUNNING。
- 通过
进行进程上下文的切换。task_switch_from_to(from, to)
task_switch_from_to(from, to)的过程
切换的时候传入两个
的地址,然后利用长跳指令跳到新的进程的TSS结构,这个TSS结构包含了新进程的上下文信息,硬件(硬件根据选择子判断是不是TSS结构)会自动将这些信息加载到各个寄存器中。\task_struct
任务切换中,cpu会把当前寄存器的数据保存到当前(旧的)tr寄存器所指向的tss数据结构里,然后把新的tss数据复制到当前寄存器里。这些操作是通过cpu的硬件实现的\
先初始化的两个任务,然后将当前任务的tss选择子存到TR寄存器中。\
注意: 下面函数的顺序并不是实际项目中的顺序,这里只是按执行的顺序放在这的,方便分析。
/**
* @brief 切换至指定任务
*/
void task_switch_from_to (task_t * from, task_t * to) {
// 简单的用jmp到对应的tss选择子进行任务切换
switch_to_tss(to->tss_sel);
// simple_switch(&from->stack, to->stack);
}
/**
* 切换至TSS,即跳转实现任务切换
*/
void switch_to_tss (int tss_sel) {
far_jump(tss_sel, 0);
}
static inline void far_jump (uint32_t selector, uint32_t offset) {
uint32_t addr[] = {offset, selector};
__asm__ __volatile__("ljmpl *(%[a])"::[a]"r"(addr));
}
就是通过tss的选择子在GDT中找到下一个任务的tss地址,然后长跳到另一个进程的tss位置,然后硬件会自动将当前TSS中的信息加载到各个寄存器,将tss位置存到TR寄存器中。
三、总结整个流程
- 1、如果就绪队列中有多个任务才进行切换。\
如果有多个任务,就先将当前任务放到就绪队列的尾部(先将任务从就绪队列中移除--
)。task_set_block(curr_task);
,然后将任务放到就绪队列尾部--
task_set_ready(curr_task);
- 2、从就绪队列中取出下一个任务。(
)task_t * to = task_next_run();
- 3、将
中当前运行的任务改为 to 任务。task_manager_t
- 4、将 to 任务的状态改为 TASK_RUNNING。
- 5、进行TSS结构的切换。\
就是通过tss的选择子在GDT中找到下一个任务的tss地址,然后长跳到另一个进程的tss位置,然后硬件会自动将当前TSS中的信息加载到各个寄存器,将tss位置存到TR寄存器中。
版权声明:
作者:徐锦桐
链接:https://www.xujintong.com/2023/10/22/43/
自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
作者:徐锦桐
链接:https://www.xujintong.com/2023/10/22/43/
自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
THE END