🐳
uCore OS(on RISC-V64)实验指导书
  • Introduction
  • LAB0:ready~go!
    • 实验目的
    • 实验内容
    • 前导知识
      • 了解uCore
      • 了解RISC-V
      • 了解OS实验
      • 了解实验环境
      • 了解开发调试基本工具
      • 了解硬件模拟器
    • 配置环境
      • 安装虚拟环境
      • 安装开发工具
      • 安装硬件模拟器
      • 安装调试工具
  • LAB0.5:最小可执行内核
    • 实验目的
    • 实验内容
    • 练习
    • 内存布局
    • 链接脚本
    • 真正的入口点
    • 从SBI到stdio
    • 编译运行
    • 项目组成与执行流
  • LAB1:中断机制
    • 实验目的
    • 实验内容
    • 练习
    • RISC-V中断相关
    • 上下文处理
    • 中断处理程序
    • 时钟中断
    • 项目组成与执行流
  • LAB2:物理内存管理
    • 实验目的
    • 实验内容
    • 练习
    • 地址与页表
    • 物理内存探测
    • 以页为单位管理物理内存
    • 页面分配算法
    • 项目组成与执行流
  • LAB3:虚拟内存管理
    • 实验目的
    • 实验内容
    • 练习
    • 页面置换
    • PageFault
    • 使用多级页表
    • 页面置换机制
    • FIFO置换算法
    • 项目组成与执行流
  • LAB4:进程管理
    • 实验目的
    • 实验内容
    • 练习
    • 进程与线程
    • 相关结构体
    • 进程模块初始化
    • 进程切换
    • 项目组成与执行流
  • LAB5:用户程序
    • 实验目的
    • 实验内容
    • 练习
    • 用户进程
    • 用户程序
    • 创建并执行用户进程
    • 系统调用
    • 用户进程的退出和等待
    • 项目组成与执行流
  • LAB6:进程调度
    • 实验目的
    • 实验内容
    • 练习
    • 进程状态
    • 再次认识进程切换
    • 调度算法框架
    • 项目组成与执行流
  • LAB7:同步互斥
    • 实验目的
    • 实验内容
    • 练习
    • 同步互斥的基本概念
    • 信号量
    • 条件变量与管程
    • 项目组成与执行流
  • LAB8:文件系统
    • 实验目的
    • 实验内容
    • 练习
    • 文件系统介绍
    • 文件系统抽象层VFS
    • 硬盘文件系统SFS
    • 设备即文件
    • 从中断到终端
    • 项目组成与执行流
由 GitBook 提供支持
在本页
  • 进程控制块
  • 进程上下文

这有帮助吗?

  1. LAB4:进程管理

相关结构体

在实现和进程相关的代码之前,我们先来实现一些数据结构来帮助我们对于进程进行管理。在本章实现的进程管理模型中,我们主要维护两个数据结构:进程控制块和进程上下文。进程控制块维护进程的各个信息,包括内存映射,进程名等等。进程上下文里面保存了和进程运行状态相关的各个寄存器的值,这些是为了之后恢复进程运行状态用的。下面我们来看一看这两个数据结构。

进程控制块

我们在ucore中使用结构体struct proc_struct来保存和进程相关的控制信息。

struct proc_struct内部结构如下:

struct proc_struct {
    enum proc_state state;                  // Process state
    int pid;                                // Process ID
    int runs;                               // the running times of Proces
    uintptr_t kstack;                       // Process kernel stack
    volatile bool need_resched;             // bool value: need to be rescheduled to release CPU?
    struct proc_struct *parent;             // the parent process
    struct mm_struct *mm;                   // Process's memory management field
    struct context context;                 // Switch here to run process
    struct trapframe *tf;                   // Trap frame for current interrupt
    uintptr_t cr3;                          // CR3 register: the base addr of Page Directroy Table(PDT)
    uint32_t flags;                         // Process flag
    char name[PROC_NAME_LEN + 1];           // Process name
    list_entry_t list_link;                 // Process link list 
    list_entry_t hash_link;                 // Process hash list
};

这里面值得我们关注的主要有以下几个成员变量:

  • parent:里面保存了进程的父进程的指针。在内核中,只有内核创建的idle进程没有父进程,其他进程都有父进程。进程的父子关系组成了一棵进程树,这种父子关系有利于维护父进程对于子进程的一些特殊操作。

  • mm:这里面保存了内存管理的信息,包括内存映射,虚存管理等内容。具体内在实现可以参考之前的章节。

  • context:context中保存了进程执行的上下文,也就是几个关键的寄存器的值。这些寄存器的值用于在进程切换中还原之前进程的运行状态(进程切换的详细过程在后面会介绍)。切换过程的实现在kern/process/switch.S。

  • tf:tf里保存了进程的中断帧。当进程从用户空间跳进内核空间的时候,进程的执行状态被保存在了中断帧中(注意这里需要保存的执行状态数量不同于上下文切换)。系统调用可能会改变用户寄存器的值,我们可以通过调整中断帧来使得系统调用返回特定的值。

  • cr3:cr3寄存器是x86架构的特殊寄存器,用来保存页表所在的基址。出于legacy的原因,我们这里仍然保留了这个名字,但其值仍然是页表基址所在的位置。

进程上下文

进程上下文使用结构体struct context保存,其中包含了ra,sp,s0~s11共14个寄存器。

可能感兴趣的同学就会问了,为什么我们不需要保存所有的寄存器呢?这里我们巧妙地利用了编译器对于函数的处理。我们知道寄存器可以分为调用者保存(caller-saved)寄存器和被调用者保存(callee-saved)寄存器。因为线程切换在一个函数当中(我们下一小节就会看到),所以编译器会自动帮助我们生成保存和恢复调用者保存寄存器的代码,在实际的进程切换过程中我们只需要保存被调用者保存寄存器就好啦!

上一页进程与线程下一页进程模块初始化

最后更新于4年前

这有帮助吗?