xv6(RISC-V)操作系统源码分析第七节——进程的调度与交互

一、调度的必要性

任何操作系统运行的进程数量都可能超过计算机的CPU数量,因此必须制定一个方案,使各进程能够分时共享CPU。这就涉及到进程的切换,这种切换就是调度。调度的目的是实现多路复用。

二、实现多路复用的注意点

多路复用是一个具有挑战性的机制。xv6采用下面技术来实现多路复用:

  • 上下文切换可以实现多路复用,但xv6中的上下文切换是最不透明的代码之一
  • xv6使用定时器中断来驱动上下文切换
  • 许多CPU会在进程间并发切换,锁可以用来避免竞争
  • 当进程退出时,必须释放进程的内存和其他资源,但进程本身不能完全释放掉所有资源,比如它不能在使用内核栈的同时释放内核栈
  • 多处理器计算机的每个CPU必须记住它正在执行的进程

三、何时调度

xv6仅在下面两种情况下会发生调度:

  • 当一个进程等待设备或管道I/O,或在sleep系统调用中等待
  • 定时器中断引起的调度

四、与进程切换有关的数据结构

xv6运行在多核处理器上,并且一个核可并发运行多个进程,为了实现进程调度等功能,我们需要数据结构来标识不同的核与不同的进程。

(一)cpu

struct cpu {
  struct proc *proc;          // The process running on this cpu, or null.
  struct context context;     // swtch() here to enter scheduler().
  int noff;                   // Depth of push_off() nesting.
  int intena;                 // Were interrupts enabled before push_off()?
};

xv6为每个CPU都维护了一个cpu结构体,它详细记录了该CPU的对xv6有用的信息。每个CPU都有一个编号——hartid,该编号保存在每个CPU的mhartid寄存器中。xv6可以通过该编号来索引分辨不同的CPU。

这里值得注意的是,如果想获得当前CPU的信息,但是这时发生了定时器中断,可能当前的执行的CPU会变成其他的CPU,那么获得的CPU信息就有误,为了保证正确性,会在读CPU信息前关闭定时器中断,读完后才会打开。

(二)process

struct proc {
  struct spinlock lock;

  // p->lock must be held when using these:
  enum procstate state;        // Process state
  void *chan;                  // If non-zero, sleeping on chan
  int killed;                  // If non-zero, have been killed
  int xstate;                  // Exit status to be returned to parent's wait
  int pid;                     // Process ID

  // wait_lock must be held when using this:
  struct proc *parent;         // Parent process

  // these are private to the process, so p->lock need not be held.
  uint64 kstack;               // Virtual address of kernel stack
  uint64 sz;                   // Size of process memory (bytes)
  pagetable_t pagetable;       // User page table
  struct trapframe *trapframe; // data page for trampoline.S
  struct context context;      // swtch() here to run process
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)
};

它的设计理念与注意事项同cpu。

五、上下文切换

上下文切换可以简单地理解为在调度前保护现场或恢复现场(保护或恢复CPU中的寄存器组),而xv6中的上下文特指以下寄存器:

  • ra
  • sp
  • s0
  • s1
  • s2
  • s3
  • s4
  • s5
  • s6
  • s7
  • s8
  • s9
  • s10
  • s11

函数swtch(使用汇编语言编写)负责内核线程切换的保护和恢复。它有两个参数第一个参数是old context,第二个参数是new context,它会将当前寄存器保存至old context中,而从new中加载寄存器并返回。

void            swtch(struct context*, struct context*);

swtch不直接知道线程,只是保护和恢复寄存器组,该寄存器组称为上下文

.globl swtch
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)

      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值