6.S081-2内核的隔离性(isolation)

6.S081 2. 内核的隔离性(isolation)

备注,很多内容复制于MIT6.S081

本节课的后续实验:6.S081 Lab00 xv6启动过程(从代码出发,了解操作系统启动过程)

1. isolation

防止内存空间被覆盖:使用操作系统的一个原因,甚至可以说是主要原因就是为了实现multiplexing和内存隔离。

如果我们从隔离的角度来稍微看看Unix接口,那么我们可以发现,接口被精心设计以实现资源的强隔离,也就是multiplexing和物理内存的隔离。接口通过抽象硬件资源,从而使得提供强隔离性成为可能。

之前通过fork创建了进程。进程本身不是CPU,但是它们对应了CPU,它们使得你可以在CPU上运行计算任务。所以你懂的,应用程序不能直接与CPU交互,只能与进程交互。操作系统内核会完成不同进程在CPU上的切换。所以,操作系统不是直接将CPU提供给应用程序,而是向应用程序提供“进程”,进程抽象了CPU,这样操作系统才能在多个应用程序之间复用一个或者多个CPU。

2. defensive

应用程序不能够打破对它的隔离。应用程序非常有可能是恶意的,它或许是由攻击者写出来的,攻击者或许想要打破对应用程序的隔离,进而控制内核。一旦有了对于内核的控制能力,你可以做任何事情,因为内核控制了所有的硬件资源。

通常来说,需要通过硬件来实现这的强隔离性。我们这节课会简单介绍一些硬件隔离的内容,但是在后续的课程我们会介绍的更加详细。这里的硬件支持包括了两部分,第一部分是user/kernel mode,kernel mode在RISC-V中被称为Supervisor mode但是其实是同一个东西;第二部分是page table或者虚拟内存(Virtual Memory)。

所有的处理器,如果需要运行能够支持多个应用程序的操作系统,需要同时支持user/kernle mode和虚拟内存

硬件隔离:

  1. user/kernle mode

  2. Virtual Memory (age table)

3. User / Kernel mode

处理器会有两种操作模式,第一种是user mode,第二种是kernel mode。当运行在kernel mode时,CPU可以运行特定权限的指令(privileged instructions);当运行在user mode时,CPU只能运行普通权限的指令(unprivileged instructions)。

User mode 可以运行的指令有 add, jmp, sub, branch, loop …

Kernel mode 的特殊指令只要是一些直接操纵硬件和设置保护的指令,比如设置page table寄存器、关闭时钟中断。(应用程序不应该执行这些指令,这些指令只能被内核执行)

在处理器的一个bit (-- flag),当它为1的时候是user mode,当它为0时是kernel mode

用户是如何获得(OS给的)CPU等资源的?-- ECALL触发软中断。当用户程序执行系统调用,会通过ECALL触发一个软中断(software interrupt),软中断会查询操作系统预先设定的中断向量表,并执行中断向量表中包含的中断处理程序。中断处理程序在内核中,这样就完成了user mode到kernel mode的切换,并执行用户程序想要执行的特殊权限指令。

(还有一小部分讲page table,这个先不写了,等page table篇统一写)

4. User / Kernel mode 切换

需要有一种方式能够让应用程序可以将控制权转移给内核(Entering Kernel) – 就像3. 中最后所讲的那样。

有一个专门的指令用来实现这个功能,叫做ECALL。ECALL接收一个数字参数,当一个用户程序想要将程序执行的控制权转移到内核,它只需要执行ECALL指令,并传入一个数字。这里的数字参数代表了应用程序想要调用的System Call。 – 我猜:这个数字应该是中断向量表中的偏移。(本科的微机原理 + 汇编)

在内核侧,有一个位于syscall.c的函数syscall,每一个从应用程序发起的系统调用都会调用到这个syscall函数,syscall函数会检查ECALL的参数,通过这个参数内核可以知道需要调用的是fork

学生提问:当应用程序表现的恶意或者就是在一个死循环中,内核是如何夺回控制权限的?

Frans教授:内核会通过硬件设置一个定时器,定时器到期之后会将控制权限从用户空间转移到内核空间,之后内核就有了控制能力并可以重新调度CPU到另一个进程中。我们接下来会看一些更加详细的细节。

5. Monolithic Kernel vs Micro Kernel (微内核vs宏内核)

微内核的目的在于将大部分的操作系统运行在内核之外。(只支持一些信息传递,pagetable,CPU分时复用等)(更少的代码意味着更少的Bug) – 在user/kernel mode反复跳转带来的性能损耗。(嵌入式多用)–在一个类似宏内核的紧耦合系统,各个组成部分,例如文件系统和虚拟内存系统,可以很容易的共享page cache。而在微内核中,每个部分之间都很好的隔离开了,这种共享更难实现。进而导致更难在微内核中得到更高的性能。

宏内核就是整个OS都运行在kernel mode(整体性更好) – 比如Linux (但是OS的bug会带来kernel的bug)

6. Kernel的编译运行 + QEMU

xv6目录下有三个主要文件夹kernel + user + mkfs(它会创建一个空的文件镜像,我们会将这个镜像存在磁盘上,这样我们就可以直接使用一个空的文件系统。)

1. kernel 的编译

  1. 选择pipe.c 调用gcc编译器,生成一个文件叫做 pipe.s (编译,生成汇编语言)

  2. 之后再走到汇编解释器,生成pipe.o (汇编,生成二进制机器语言)

  3. 链接(Loader):将pipe.o 和 各种.o链接起来,比如proc.o(由proc.c生成得到) – 最终形成Kernel

Makefile还会创建kernel.asm,这里包含了内核的完整汇编语言,你们可以通过查看它来定位究竟是哪个指令导致了Bug。

Kernel.asm 的部分代码如下所示(总共有14839行)

kernel/kernel:     file format elf64-littleriscv


Disassembly of section .text:

0000000080000000 <_entry>:
    80000000:	0000b117          	auipc	sp,0xb
    80000004:	80010113          	addi	sp,sp,-2048 # 8000a800 <stack0>
    80000008:	6505                	lui	a0,0x1
    8000000a:	f14025f3          	csrr	a1,mhartid
    8000000e:	0585                	addi	a1,a1,1
    80000010:	02b50533          	mul	a0,a0,a1
    80000014:	912a                	add	sp,sp,a0
    80000016:	070000ef          	jal	ra,80000086 <start>

000000008000001a <junk>:
    8000001a:	a001                	j	8000001a <junk>

000000008000001c <timerinit>:
// which arrive at timervec in kernelvec.S,
// which turns them into software interrupts for
// devintr() in trap.c.
void
timerinit()
{
    8000001c:	1141                	addi	sp,sp,-16
    8000001e:	e422                	sd	s0,8(sp)
    80000020:	0800                	addi	s0,sp,16
// which hart (core) is this?
static inline uint64
r_mhartid()

第一个指令位于地址0x80000000,对应的是一个RISC-V指令:auipc指令。所以这里0x0000a117就是auipc,这里是二进制编码后的指令。

2. QEMU – RISCV的软件模拟器(有点像本科的嵌入式,先软件模拟,然后烧板子)

img

RISCV的系统架构图↑

这个图里面有:

  • 4个核:U54 Core 1-4
  • L2 cache:Banked L2
  • 连接DRAM的连接器:DDR Controller
  • 各种连接外部设备的方式,比如说UART0,一端连接了键盘,另一端连接了terminal。
  • 以及连接了时钟的接口:Clock Generation

在内部,在QEMU的主循环中,只在做一件事情:

  • 读取4字节或者8字节的RISC-V指令。

  • 解析RISC-V指令,并找出对应的操作码(op code)。我们之前在看kernel.asm的时候,看过一些操作码的二进制版本。通过解析,或许可以知道这是一个ADD指令,或者是一个SUB指令。

  • 之后,在软件中执行相应的指令。

QEMU:

for (;;) {
	read instruction;
	decode instruction; (into asm: add / sub / jmp ...)
	execute instruction;
}

7. XV6启动过程

准备当成一个实验来做,可以看后面的博客

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值