一个操作系统至少应该满足三个需求:多路复用、隔离、交互。本章主要介绍如何组织操作系统来实现以上的三个需求,本文关注的是一种围绕单核进行设计的方法,这种设计是被许多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. shopify本地开发 新的地方

    看了一些教程有些旧地方通不过,自己摸索下 1.创建app 点击Settings,再点击Apps and sales channels 点Allow custom app development 继续 ...

  2. 题解 ARC104F

    前言 在这里首先感谢一下题解区的 FZzzz,本人的题解思路主要是基于他并给出了自己的理解. 如非特殊说明,本题解中的数学符号原则上与题目中一致. 题目分析 需要转化的喵喵题. 我们需要把原问题转化成 ...

  3. timeSetEvent()函数定时器的使用

    1.定时器函数的使用 微软公司在其多媒体Windows中提供了精确定时器的底层API支持,利用多媒体定时器可以很精确地读出系统的当前时间,并且能在非常精确的时间间隔内完成一个事件.函数或过程的调用. ...

  4. Oracle数据库卸载器 - 开源研究系列文章

    今天无事,把网上搜到的Oracle数据库卸载器的软件更新到C#的Winform界面的操作上. 1. 程序目录: 与笔者的其它软件类似,目录如下: 2. 使用的类: 这里主要使用了一个处理函数: 3. ...

  5. C++ Qt 开发:ListWidget列表框组件

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍ListWid ...

  6. 春秋云镜 - CVE-2022-28512

    Fantastic Blog (CMS)是一个绝对出色的博客/文章网络内容管理系统.它使您可以轻松地管理您的网站或博客,它为您提供了广泛的功能来定制您的博客以满足您的需求.它具有强大的功能,您无需接触 ...

  7. 查看电脑、手机中已保存的wifi密码

    电脑: 以管理员身份运行CMD,执行 netsh wlan show profile netsh wlan export profile folder=C:\ key=clear 此时,用记事本打开对 ...

  8. ESP32 IDF iic通信( 已验证) C语言

    关于iic原理建议B站自己看视频去, 然后本文主要实现了esp32的初始化, 写地址, 写数据, 读数据的功能, 从机的代码因为展示不需要,没写. 园子里面有个兄弟写了iic的代码.但是里面有点毒,多 ...

  9. st_geometry、st_transform配置及问题汇总

    1.文件配置修改 路径上一定要使用双斜杠 2.使用sde账号登录,执行以下SQL,路径按照实际环境填写. select * from user_libraries; create or replace ...

  10. 一篇文章彻底搞懂TiDB集群各种容量计算方式

    背景 TiDB 集群的监控面板里面有两个非常重要.且非常常用的指标,相信用了 TiDB 的都见过: Storage capacity:集群的总容量 Current storage size:集群当前已 ...