手写操作系统项目----进程切换

本文详细解释了Linux内核中进程切换的过程,包括task.h中的任务管理结构、sys_sched_yield函数的工作原理,以及task_dispatch和task_switch_from_to函数如何实现任务上下文切换,涉及中断保护、就绪队列操作和TSS/GDT的使用。
摘要由CSDN通过智能技术生成

大家好,我叫徐锦桐,个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识,还有日常折腾的经验,欢迎大家访问。

讲解了项目中是如何进程进程切换的。

一、宏观进程切换

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;
  • task_manager_t中当前运行的任务改为to任务。
  • 将to任务的状态改为TASK_RUNNING。
  • 通过task_switch_from_to(from, to)进行进程上下文的切换。

task_switch_from_to(from, to)的过程

切换的时候传入两个task_struct的地址,然后利用长跳指令跳到新的进程的TSS结构,这个TSS结构包含了新进程的上下文信息,硬件(硬件根据选择子判断是不是TSS结构)会自动将这些信息加载到各个寄存器中。


任务切换中,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、将task_manager_t中当前运行的任务改为 to 任务。
  • 4、将 to 任务的状态改为 TASK_RUNNING。
  • 5、进行TSS结构的切换。
    就是通过tss的选择子在GDT中找到下一个任务的tss地址,然后长跳到另一个进程的tss位置,然后硬件会自动将当前TSS中的信息加载到各个寄存器,将tss位置存到TR寄存器中。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐锦桐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值