riscv-xv6是基于多核的riscv指令架构的教学用操作系统的实现,这篇短文将在已有相关启动流程介绍[1]的基础上补充介绍riscv-xv6。
1、入口_entry的汇编代码见entry.S[2],_entry的代码会加载在kernel加载到内存的起始位置0x80000000,每个hart(hart 的全称是 Hardware Thread,即硬件线程。在 xv6 操作系统中,hart 通常用于表示一个 CPU 核心)会调用start.c的代码执行。在 xv6 操作系统中,stack0 是一个用作临时工作栈的区域(其为全局变量,在内核的数据区),特别是在内核早期启动阶段,操作系统还没有设置完进程的内核栈时。当设置了分页功能,启动了第一个用户进程后,操作系统的内核将会运行在kstack特定的栈区(不同的进程可以并行运行在不同的进程对应的内核栈区位置)。
2、start.c的代码中相关的知识点:mstatus时machine status register,MSTATUS 寄存器用于存储 CPU 的状态信息,包括当前执行模式和中断状态等信息,其代码及注释参考[3]和[4]。
3、这里主要解释main.c函数的相关处理逻辑。start.c的最后通过调用mret指令,进入main函数[5],其中kinit函数(代码见kernel/kalloc.c)实现了物理内存的初始化(将从end到PHYSTOP的内存空间按照PAGESIZE大小为单元串接成空闲页链表),kvminit(kernel/vm.c)实现了内核页表的生成和虚拟地址到物理地址的直接映射;kvminithart函数将内核页表地址设置到SATP寄存器从而使能分页功能(turn on paging);procinit函数初始化进程表(编译时生成在内核的数据区),以及进程表中每一个proc结构中对应的内核栈地址,在 riscv-xv6 或其他类似操作系统中,内核的数据区中的一些结构(例如由 kalloc 管理的内存块列表)是 动态变化的,并且内核的数据区是允许这种变化的。kalloc(内存分配器)管理的内存块列表和其他数据结构,虽然会动态修改数据内容(如指针更新、内存块分配和回收),但是并不需要为这些数据结构的变化分配新的内存空间。因此,内核的数据区的大小可以被认为是固定的,也就是说,数据区的大小在系统启动时就已经确定,并且在运行过程中不会扩展或缩小。trapinit函数初始化计时器相关的自旋锁,在xv6系统中有一个全局变量ticks。trapinithart函数将kernelvec的代码段初始地址赋值给stvec寄存器,以便在内核态能响应中断处理(kerneltrap函数进一步会调用devintr函数),关于文件系统和persistent memory驱动相关的内容,后面的文章将有专门介绍。关于user/initcode和不同cpu核的初始化顺序问题,可以分别参考引文[6]和[7]。初始化过程之后,每一个cpu都将调用scheduler()函数调度进程开始执行。
问答:
1、问题:不同的cpu共享内核页,每个cpu的stvec寄存器共享kernelvec和uservec代码段,由于这些代码没有共享变量,为可重入代码,因此可以方便共享,是可以这么理解对吗
回答:kernelvec和uservec分别为在内核态和用户态发生trap时所需要的处理逻辑,没有共享变量(tramponline.S的uservec和kernelvec.S的kernelvec),每个cpu运行有自己的内核栈kstack和trapframe结构体数据,因此不同的cpu的内核态中断处理和用户态中断处理代码可以共享。
2、问题:每个cpu是不是有其自己的定时器,从定时器的中断处理来看,好像只对0号cpu的定时器中断做了处理?
回答:是的,选择0号cpu处理时间中断,方便了系统统一的时间计数和进程调度处理,虽然在riscv等芯片实现中每个cpu核有自己的定时器,但操作系统选择在0号cpu上统一处理,方便了集中管理时间和进程调度。在芯片的设计中,cpu的每个核心之间有共享cache,cache的作用在于快速的获取数据,根据存储访问速度来看,cpu访问cache的速度比内存的速度快多个数量级。由于进程调度的出现,同一个进程(线程)可能在不同的cpu上运行,因此cpu共享cache是有必要的,但cache也分级数(尽管每个 CPU 核心有自己的 L1 和 L2 缓存,但它们通常共享 L3 缓存。这意味着在多核系统中,多个 CPU 核心之间可以共享 L3 缓存来加速数据访问。但由于每个 CPU 核心的 L1 和 L2 缓存是独立的,因此数据迁移和缓存一致性依然是一个重要问题。)。不同cpu共享内核的代码,但有其独立运行的内核栈,其逻辑计算相对独立且可以并行执行。
References
- [1]、xv6启动流程分析(Code: starting xv6, the first process and system call)
- [2]、entry.S入口汇编代码:https://gitee.com/kindlytree/xv6-labs-2024/blob/util/kernel/entry.S#L7
- [3]、[mstatus寄存器标记状态示意图]:https://gitee.com/kindlytree/riscv-xv6/issues/IB8LJR#note_35014491_link
- [4]、start.c的代码及注释:https://gitee.com/kindlytree/riscv-xv6/issues/IB8LJR#note_34916069_link
- [5]、main函数实现:https://gitee.com/kindlytree/xv6-labs-2024/blob/riscv/kernel/main.c#L19
- [6]、user/initcode程序的16进制内容:https://gitee.com/kindlytree/riscv-xv6/issues/IB8LJR#note_36917603_link
- [7]、main.c中不同cpu核初始化的代码同步问题:https://gitee.com/kindlytree/riscv-xv6/issues/IB8LJR#note_36917605_link
Leave a Reply