xv6 risc-v scheduler 笔记

本文详细探讨了xv6在RISC-V架构上的进程调度机制,包括上下文切换、调度器的工作方式、进程状态转换以及sleep/wakeup函数的使用。文章深入分析了在多核环境下的调度安全性和并发问题,并通过代码分析解释了如何通过switch.S保存和恢复寄存器。此外,还讨论了管道(pipes)、wait、exit、kill等系统调用的实现细节,以及它们与进程调度的关系。
摘要由CSDN通过智能技术生成

multiplexing

xv6的进程调度通过两个机制实现:程序通过调用sleep/wakeup进行的自主切换 和 定时中断驱动的强制切换

要进行对用户进程透明的方式进行进程切换,就需要进行上下文切换,而为了维护切换过程中的不变量(invariant),还需要在适当的时候加锁

xv6中上下文切换如图:

用户进程trap到该进程的内核线程(内核态),然后进行上下文切换(这里的切换,和前面的切换,区别在于,后者是通过函数调用switch函数,而前者不是,所以后者切换时只需要保存callee-saved register和ra,sp,而前者需要保存全部的寄存器)到cpu的调度器线程,调度器线程选出下一个要运行的进程,上下文切换到该进程的内核栈,然后trap return到该进程的用户线程,这样,完成了一个进程到另一个进程的切换

code:context switching

上下文切换需要保存原进程的寄存器(内存不用管,因为不同进程的内存不会重叠,他们映射到不同区域),我们需要保存的是callee-saved寄存器,以及返回地址ra,栈顶指针sp,根据riscv的函数调用约定,他们是:

// Saved registers for kernel context switches.
struct context {
  uint64 ra;
  uint64 sp;

  // callee-saved
  uint64 s0;
  uint64 s1;
  uint64 s2;
  uint64 s3;
  uint64 s4;
  uint64 s5;
  uint64 s6;
  uint64 s7;
  uint64 s8;
  uint64 s9;
  uint64 s10;
  uint64 s11;
};

真正执行保存恢复寄存器的是switch.S,他有两个参数,分别指向旧进程context和新进程context(保存在a0,a1里)

swtch:
        sd ra, 0(a0)
        sd sp, 8(a0)
        sd s0, 16(a0)
        sd s1, 24(a0)
        sd s2, 32(a0)
        sd s3, 40(a0)
        sd s4, 48(a0)
        sd s5, 56(a0)
        sd s6, 64(a0)
        sd s7, 72(a0)
        sd s8, 80(a0)
        sd s9, 88(a0)
        sd s10, 96(a0)
        sd s11, 104(a0)

        ld ra, 0(a1)
        ld sp, 8(a1)
        ld s0, 16(a1)
        ld s1, 24(a1)
        ld s2, 32(a1)
        ld s3, 40(a1)
        ld s4, 48(a1)
        ld s5, 56(a1)
        ld s6, 64(a1)
        ld s7, 72(a1)
        ld s8, 80(a1)
        ld s9, 88(a1)
        ld s10, 96(a1)
        ld s11, 104(a1)
        
        ret

switch.S的内容很简单,即根据结构体不同成员变量的偏移量(都是uint64,所以都是相隔8个字节)进行保存,加载

值得注意的是,最后面的ret,它将ra的内容设置为pc,注意这里的ra是要切换到的进程之前执行switch的时候保存的ra(可以看到基地址分别时a0和a1,对应第一第二个函数参数),也就是此次执行完switch之后,不是接着当前位置的下一行运行,而是另一处switch的下一行运行,举xv6的做法为例说明:

xv6中只有两处调用switch:

void
sched(void)
{
    ....

  swtch(&p->context, &mycpu()->scheduler);
  
    ....
}
void
scheduler(void)
{
   ...
   swtch(&c->scheduler, &p->context);
   ...
}

可以看出这里没有两个用户进程之间的直接切换,只有用户进程和调度器线程之间的切换

xv6中要主动让出cpu的进程都是通过调用exit

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值