一个操作系统至少应该满足三个需求:多路复用、隔离、交互。本章主要介绍如何组织操作系统来实现以上的三个需求,本文关注的是一种围绕单核进行设计的方法,这种设计是被许多uinx操作系统所使用的。Xv6运行在多核RISC-V微处理器上时,它的许多低级功能(例如,它的进程实现)是特定于RISC-V的,RISC-V是一个64位的处理器,而xv6是用“LP64”C语言编写的,这意味着C编程语言中的长(L)和指针(P)是64位,而int是32位,这里顺便复习一下指针的大小是和机器有关的,比如64位机器中指针大小就是8字节。xv6是为qemu的“-machine virt”选项模拟的支持硬件编写的。这包括RAM、包含引导代码的ROM、与用户键盘/屏幕的串行连接以及用于存储的磁盘,而qemu所模拟的环境是单个CPU内核

1 抽象物理资源

为什么要有操作系统?操作系统可以做为一个系统调用的库,被所有运行在里面的程序所链接,而程序可以通过操作系统更好的使用硬件资源。通常进程总是会有bug并且不能很好的信任彼此,因此需要很好的隔离性。隔离性不仅仅指进程之间,还有进程与资源,进程与操作系统。

为了实现强隔离,禁止应用程序直接访问敏感的硬件资源,将资源抽象到服务中,是一种很好的方法。例如,Unix应用程序仅通过文件系统的open、read、write和close系统调用与存储交互,而不是直接读写磁盘。这样一来,操作系统才是真正的资源管理者,而不是用户。

2 用户模式、管理者模式以及系统调用

强隔离要求应用程序和操作系统之间有一个硬边界。如果应用程序出错,我们不希望操作系统失败,也不希望其他应用程序失败。操作系统应该能够清理失败的应用程序并继续运行其他应用程序。为了实现强隔离,操作系统必须安排应用程序不能修改(甚至读取)操作系统的数据结构和指令,并且应用程序的指令不能访问其他进程的内存。这里其实就开始引出为什么要有用户态和内核态了。

cpu为强隔离提供硬件支持。例如,RISC-V有三种CPU执行指令的模式:机器模式、管理者模式和用户模式。在机器模式下执行的指令具有完全权限;CPU以机器模式启动。机器模式主要用于配置计算机。Xv6在机器模式下执行几行代码,然后切换到管理者模式。

在管理者模式下,CPU被允许执行特权指令:例如,启用和禁用中断,读取和写入保存页表地址的寄存器,等等。如果用户模式下的应用程序试图执行特权指令,则CPU不会执行该指令,而是切换到管理者模式,以便管理者模式代码可以终止应用程序,因为它做了一些不应该做的事情。应用程序只能执行用户模式指令(例如,添加数字等),被称为在用户空间中运行,而处于管理者模式的软件也可以执行特权指令,被称为在内核空间中运行。在内核空间(或在管理器模式下)运行的软件称为内核。

上述三种模式是针对于CPU的,对于程序而言,它所执行的环境可以分为用户态和内核态,对应着CPU的用户模式和管理者模式。一个想要调用内核函数的应用程序(例如,在xv6中读取系统调用)必须转换到内核态。CPU通常会提供一个特殊的指令,可以将CPU从用户模式切换到管理模式,并在内核指定的入口点进入内核。(RISC-V为此目的提供了调用指令ecall)。

一旦CPU切换到管理者模式,内核就可以验证系统调用的参数(例如,检查传递给系统调用的地址是否属于应用程序内存的一部分),决定是否允许应用程序执行请求的操作(例如,检查是否允许应用程序写入指定的文件),然后拒绝它或执行它。重要的是,内核会控制过渡到管理器模式的入口点,否则恶意程序也可以随意进入内核态

3 内核架构

一个关键的设计问题是操作系统的哪个部分应该在管理者模式下运行。一种实现是整个操作系统驻留在内核中,因此所有系统调用的实现都以管理者模式运行。这种组织称为宏内核(a monolithic kernel)。

在这种组织中,整个操作系统以完全硬件特权运行。这种组织很方便,因为操作系统设计者不必决定操作系统的哪一部分不需要完全的硬件特权。此外,操作系统的不同部分更容易协作。例如,一个操作系统可能有一个缓冲缓存,它可以被文件系统和虚拟内存系统共享。

但宏内核的缺点就是操作系统中不同部分之间的接口设计很复杂,并且宏内核中产生的任何一个错误都是致命的,因为管理者模式中的错误通常都会导致cpu运行失败,这个时候必须重启才行。

为了减少在内核中出错的风险,操作系统设计者可以尽量减少在管理者模式下运行的操作系统代码的数量,并在用户模式下执行大部分操作系统。这种内核组织称为微内核



在图中,文件系统作为用户级进程运行。作为进程运行的操作系统服务称为服务器。为了允许应用程序与文件服务器交互,内核提供了一个进程间通信机制,将消息从一个用户模式进程发送到另一个用户模式进程。例如,如果像shell这样的应用程序想要读取或写入文件,它会向文件服务器发送消息并等待响应。

但无论是微内核还是宏内核,都是有很多相似的设计思想,比如它们都会有系统调用,处理中断、支持进程,支持锁,实现文件系统等。Xv6是作为一个宏内核实现的,因此,xv6内核接口对应于操作系统接口,内核实现完整的操作系统。

4 内核的文件组织

xv6内核源代码位于“kernel/”子目录下。源代码按照模块化的方式粗略划分为多个文件,如下图所示,模块间的接口定义在kernel/defs.h中

5 进程概述

进程是xv6内核中最小的隔离单位,内核用来实现进程的机制包括用户/主管模式标志、地址空间和线程的时间切片。Xv6使用页表(由硬件实现)为每个进程提供自己的地址空间。RISC-V页表转换(或“映射”)一个虚拟地址(RISC-V指令操作)到一个物理地址(CPU芯片发送到主存的地址)。

xv6为每个进程维护一个单独的页表,用于定义该进程的地址空间。地址空间包括从虚拟地址0开始的进程用户内存,如下图所示。首先是指令,然后是全局变量,然后是堆栈,最上面有两个特殊的区域trapframe和trampoline,这里对其分别解释一下:

1.trapframe:

  • trapframe是一个数据结构,用于保存处理中断或异常时 CPU 寄存器的状态。它记录了中断或异常发生时,CPU 寄存器的值,以及一些其他与处理中断相关的信息。以便在中断服务例程(interrupt service routine,ISR)执行完毕后,能够正确地恢复进程的执行。
  • trapframe 中的信息可能包括程序计数器(PC)、堆栈指针(SP)、标志寄存器(FLAGS)、各种段寄存器等。

2.trampoline:

  • trampoline是一段代码,通常是汇编代码,用于在用户态和内核态之间进行切换。当中断或异常发生时,CPU 需要从用户态切换到内核态,执行内核中对应的中断服务例程。trampoline 代码的作用是协助这个切换过程。
  • 在 xv6 中,trampoline 代码的一部分被放置在用户地址空间中,以便在用户程序发生中断时,能够正确地切换到内核态执行中断服务例程。

RSIC-V处理器上的指针是64位的,但在页表中查找虚拟地址的时候,硬件只使用低39位,因此进程空间的最大地址只能是2^38-1,对于最大地址空间的宏定义为MAXVA(kernel/riscv.h:363)

xv6内核为每个进程维护一个proc结构体,用于保存进程的一些状态信息

(kernel/proc.h:86)。进程最重要的内核状态信息是他的页表、堆栈和运行状态。可以使用p->xxx来指代proc结构体的元素,如p->pagetable是指向进程页表的指针。

每个进程都有一个用于执行指令的上下文。这个上下文包括进程的寄存器状态、程序计数器(PC)以及其他与执行指令相关的信息。每个进程都在自己的地址空间中执行,有独立的寄存器状态和代码空间。而xv6book中所指的执行线程可以理解为上下文中可以执行指令的组成部分。每个进程有两个堆栈:用户堆栈和内核堆栈(p->kstack)。当进程执行用户指令时,只有它的用户堆栈在使用,而它的内核堆栈是空的。当进程进入内核时(对于系统调用或中断),内核代码在进程的内核堆栈上执行;当进程处于内核中时,它的用户堆栈仍然包含保存的数据,但是没有被积极使用。进程的线程在主动使用其用户堆栈和内核堆栈之间交替。内核堆栈是独立的(并且不受用户代码的影响),因此即使进程破坏了它的用户堆栈,内核也可以执行。

进程可以通过执行RISC-V调用指令来进行系统调用。该指令提高硬件特权级别,并将程序计数器更改为内核定义的入口点。入口点的代码切换到内核堆栈,并执行实现系统调用的内核指令。当系统调用完成时,内核切换回用户堆栈,并通过调用sret指令返回到用户空间,这降低了硬件特权级别,并在系统调用指令之后恢复执行用户指令。进程的执行线程可以在内核中“阻塞”以等待I/O,并在I/O完成后恢复到它离开的地方。

p->state用于指示线程是在运行态、就绪态还是阻塞态,p->pagetable指示进程的页表,当在用户空间中执行进程时,xv6的分页硬件会使用该页表。而一个进程的页表不仅用于映射虚拟地址到物理地址,同时也记录了分配给该进程存储内存的物理页面的地址。

总而言之,一个进程捆绑了两个设计思想:一个地址空间,给进程一个自己内存的错觉;一个执行线程,给进程一个自己CPU的错觉。在xv6中,一个进程由一个地址空间和一个线程组成。在实际操作系统中,一个进程可能有多个线程来利用多个cpu。

xv6book阅读 chapter2的更多相关文章

  1. 《Linux内核设计与实现》CHAPTER1,2阅读梳理

    <Linux内核设计与实现>CHAPTER1,2阅读梳理 [学习时间:2.5hours] [学习内容:Linux内核简介——历史与现今版本:Linux内核源代码以及编译] CHAPTER1 ...

  2. 基于React实现的【绿色版电子书阅读器】,支持离线下载

    代码地址如下:http://www.demodashi.com/demo/12052.html MyReader 绿色版电子书阅读器 在线地址:http://myreader.linxins.com ...

  3. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  4. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

  5. 【原】FMDB源码阅读(一)

    [原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...

  6. 【原】AFNetworking源码阅读(六)

    [原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...

  7. 【原】AFNetworking源码阅读(五)

    [原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...

  8. 【原】AFNetworking源码阅读(四)

    [原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...

  9. 【原】AFNetworking源码阅读(三)

    [原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...

  10. 【原】AFNetworking源码阅读(二)

    [原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...

随机推荐

  1. re2-cpp-is-awesome

    没做出来,看题解得 攻防世界逆向高手题之re2-cpp-is-awesome_align 20h-CSDN博客 注意 汇编知识 align 8,align num是让后面的字节都对齐num,也就是这里 ...

  2. GPTs破冰硅基文明社会

    GPTs破冰硅基文明社会 渐进是技术革命的常态 技术革命看似一夕之间就颠覆了世界,但实际上每项重大技术进步的背后,都经历了漫长的渐进积累.以蒸汽机为例,最初动力微弱.效率低下,需要大量工程师跟车维护, ...

  3. 使用 Hexo 搭建个人博客并部署到云服务器

    目录 1 整体流程 2. 本地环境准备 2.1 安装 Node.js 和 Git 2.2 安装 Hexo 3. 服务端环境准备 3.1 Nginx 环境配置 3.1.1 安装 Nginx 3.1.2 ...

  4. 计算机网络之防火墙和Wlan配置

    一.防火墙 防火墙(firewall)是一种安全设备,它的位置一般处于企业网络边界与外网交界的地方,用于隔离不信任的数据包 准确点讲,它就是隔离外网和内网的一道屏障,用于保护内部资源信息安全的一种策略 ...

  5. 【scipy 基础】--空间计算

    scipy.spatial子模块提供了一系列用于处理和计算空间数据和几何形状的算法和工具,在许多领域都有广泛的应用,例如计算机视觉.地理信息系统.机器人学.医学影像分析等. 下面,来具体看看scipy ...

  6. .NET Conf 2023 Chengdu - 成都会场即将到来!

    12月9日 天府之国 不见不散 今年的.NET Conf 2023,中国区首次有两个会场举办Local Event,北京会场12月16日,成都会场12月9日.这是所有中国.NET开发者的节日,成都会场 ...

  7. jmeter二次开发自定义函数助手

    需求:在工作中,需要使用唯一的字符串来作为订单ID,于是想到了UUID,要求uuid中不能有特殊字符包括横线,所以就有了重新写一个uuid进行使用: 准备:idea 依赖包: 注意事项:必须有包且包的 ...

  8. .net 获取客户端真实ip

    Nginx 如何设置 情况1 在只有1层nginx代理的情况下,设置nginx配置"proxy_set_header X-Forwarded-For $remote_addr;". ...

  9. 【开源项目推荐】-支持GPT的智能数据库客户端与报表工具——Chat2DB

    2023年是人工智能爆火的一年,ChatGPT为首的一系列的大模型的出现,让生成式人工智能彻底火了一把.但有人会说,GPT对于我们数据开发来说并没有什么作用啊? 今天为大家推荐的开源项目,就是GPT在 ...

  10. EF Core助力信创国产数据库

    前言 国产数据库作为国产化替代的重要环节,在我国信创产业政策的指引下实现加速发展,我们国产数据库已进入百花齐放的快速发展期,相信接触到涉及到政府类等项目的童鞋尤为了解,与此同时我们有一部分也在使用各种 ...