MIT 6.828 操作系统工程 lab3A:用户环境和异常处理

本文记录了作者在实现MIT 6.828实验三中遇到的问题及解决方案,涉及用户环境创建、异常处理、系统调用等方面。作者探讨了内核如何跟踪用户环境,分配和运行环境,以及处理中断和异常。在实验过程中,作者发现并解决了因memset导致的全局变量被覆盖的问题。实验内容包括设置数据结构、加载用户程序、初始化环境数组、分配和映射内存,以及处理中断和异常的基础知识。文章还讨论了x86中断和异常机制,并介绍了设置IDT的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇是我自己探索实现 MIT 6.828 lab3A 的笔记记录,会包含一部分代码注释和要求的翻译记录,以及踩过的坑/个人的解决方案

这里是我实现的完整代码仓库,也包含其他笔记等等:https://github.com/yunwei37/6.828-2018-labs

目录

在本实验中,您将实现运行受保护的用户模式环境(即“进程”)所需的基本内核功能。您将增强JOS内核,以设置数据结构来跟踪用户环境,创建单个用户环境,将程序映像加载到其中并开始运行。您还将使JOS内核能够处理用户环境发出的任何系统调用并处理它引起的任何其他异常。

实验3包含新的源文件,可以先浏览一下:

  • inc/ env.h 用户态环境的公共定义
    • trap.h 陷阱处理的公共定义
    • syscall.h 从用户态到内核的系统调用的公共定义
    • lib.h 用户态支持库的公共定义
  • kern/ env.h 用户态环境的内核专用定义
    • env.c 实现用户态环境的内核代码
    • trap.h 内核专用陷阱处理定义
    • trap.c 陷阱处理代码
    • trapentry.S 汇编语言陷阱处理程序入口点
    • syscall.h 系统调用处理的内核专用定义
    • syscall.c 系统调用实现代码
  • lib/ Makefrag Makefile片段,用于构建用户态库
    • entry.S 用户环境的汇编语言入口点
    • libmain.c 从entry.S调用的用户态库设置代码
    • syscall.c 用户态系统调用存根函数
    • console.c putchar和getchar的用户模式实现 ,提供控制台I/O
    • exit.c exit的用户模式实现
    • panic.c 用户模式panic的实现
  • user/ * 各种测试程序来检查内核 lab3 代码

课程主页也提到了内联汇编。

记录一个奇怪的问题

在开始阶段我把代码 merge 到 lab3 分支中,开始运行的时候,发现会出现:

kernel panic at kern/pmap.c:154: PADDR called with invalid kva 00000000

经过打 log,发现在 memset 之后,会把 kern_pgdir 的值覆盖掉;

kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
memset(kern_pgdir, 0, PGSIZE);

继续打log:cprintf("%x\n",&kern_pgdir); 发现 kern_pgdir 这个变量的地址是在 f018f00c…

然而 extern char end[]; 的值是 f018f000…所以就会出现类似的问题,就是 memsetkern_pgdir 全局变量的值覆盖掉了;继续深入探讨,发现在 pmap.c 中如果是有 static 修饰的变量值是低于 end 的,如果没有修饰的话值是高于 end 的…在其他文件里面测试类似的变量也能得出类似的结果

但是从Git记录也可以看出,我们并没有修改过 kern/kernel.ld ,前面几个lab的实现也是正常的;

所以为什么会是这样呢(我也不懂)

不过对于 kern/kernel.ld 来说,我们可以看到文件记录中上一个版本有根据新版 GCC 做一些适配,因此修改了 kernel.ld,这里尝试回退一下:

	.bss : {
		PROVIDE(edata = .);
		*(.bss)
		BYTE(0)
	}

	PROVIDE(end = .);

看起来目前是可以用了。

A部分:用户环境和异常处理

新的包含文件 inc/env.h 包含JOS中用户环境的基本定义,内核使用Env数据结构来跟踪每个用户环境。在本实验中,您最初将仅创建一个环境,但是您将需要设计JOS内核以支持多个环境;实验4将允许用户环境进入fork其他环境,从而利用此功能。

内核维护了与环境有关的三个主要全局变量:

  • struct Env *envs = NULL; // 所有环境
  • struct Env *curenv = NULL; // 当前环境
  • static struct Env *env_free_list; // 可用环境链表

下面是一些细节上的描述:

  • 一旦JOS启动并运行,envs指针将指向Env代表系统中所有环境的结构数组。JOS内核将最多支持 NENV 个活动环境。
  • JOS内核将所有非活动Env结构保留在env_free_list上。这种设计可以轻松分配和释放环境,因为只需将它们添加到空闲列表中或从空闲列表中删除.
  • 内核使用该curenv符号在任何给定时间跟踪当前正在执行的环境。在启动期间,在运行第一个环境之前, curenv初始设置为NULL。

环境的状态

该 Env 结构在 inc/env.h 中定义如下:

struct Env {
   
   
	struct Trapframe env_tf;	// Saved registers
	struct Env *env_link;		// Next free Env
	envid_t env_id;			// Unique environment identifier
	envid_t env_parent_id;		// env_id of this env's parent
	enum EnvType env_type;		// Indicates special system environments
	unsigned env_status;		// Status of the environment
	uint32_t env_runs;		// Number of times environment has run

	// Address space
	pde_t *env_pgdir;		// Kernel virtual address of page dir
};

以下是这些 Env 字段的用途:

  • env_tf:

    inc/trap.h 中定义的该结构在该环境不运行时(即,在内核或其他环境正在运行时)保存该环境的已保存寄存器值。从用户模式切换到内核模式时,内核会保存这些设置,以便以后可以从中断的位置恢复环境。

  • env_link:

    这是下一个链接Env上 env_free_list。 env_free_list指向列表中的第一个可用环境。

  • env_id:

    内核在此处存储一个值,该值唯一地标识当前正在使用此Env结构的环境。

  • env_parent_id:

    内核在此处存储env_id 创建该环境的环境的。这样,环境可以形成“家谱”,这对于制定允许哪些环境对谁做事的安全性决策很有用。

  • env_type:

    这用于区分特殊环境。对于大多数环境,它将为ENV_TYPE_USER。在以后的实验中,我们将为特殊的系统服务环境引入更多类型。

  • env_status:

    此变量保存以下值之一:

    • ENV_FREE:表示该Env结构处于非活动状态,因此位于 env_free_list 上。
    • ENV_RUNNABLE:指示该Env结构表示正在等待在处理器上运行的环境。
    • ENV_RUNNING:指示该Env结构代表当前正在运行的环境。
    • ENV_NOT_RUNNABLE:指示该Env结构表示当前处于活动状态的环境,但是当前尚未准备好运行:例如,它正在等待来自另一个环境的进程间通信(IPC)。
    • ENV_DYING:指示该Env结构表示僵尸环境。僵尸环境在下一次捕获到内核时将被释放。
  • env_pgdir:

    此变量保存 此环境的页目录的内核虚拟地址。

像Unix进程一样,JOS环境将 “线程” 和 “地址空间” 的概念结合在一起。线程主要由保存的寄存器(env_tf字段)定义,地址空间由 env_pgdir 指向的页目录和页表定义。要运行环境,内核必须设置CPU、保存的寄存器和相应的地址空间。

我们 struct Env 类似于 xv6 的 struct proc。这两个结构都在一个结构中保留环境(即进程)的用户模式寄存器状态Trapframe 。在JOS中,各个环境不像xv6中的进程那样具有自己的内核堆栈。只能有一个JOS环境在内核中被激活,所以 JOS 只需要有一个 单一的内核堆栈。

分配环境数组

现在,您将需要进一步修改 mem_init() 以分配一个相似结构数组envs。这也是练习1的内容。

开始写代码:这部分也是和 lab2 类似,根据提示即可:

...
envs = (struct Env*)boot_alloc(NENV * sizeof(struct Env));
...
boot_map_region(kern_pgdir
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值