MIT 6.828 JOS/XV6 LAB4-partC

这一部分要实现抢占式调度和进程间通信

前面的调度是进程资源放弃CPU,但是实际中没有进程会这样做的,而为了不让某一进程耗尽CPU资源,需要抢占式调度,也就需要硬件定时

但是外部硬件定时在Bootloader的时候就关闭了,至今都没有开启

而JOS采取的策略是,在内核中的时候,外部中断是始终关闭的,而在用户态的时候,需要开启中断

所以首先需要求改IDT,但是我在之前把256个都弄好了

所以现在只需要在进入用户态的时候,保证外部中断是使能的就可以了,为了做到这一点,可以在每个进程初始化的时候就将外部中断使能位置位

在kern/env.c的env_alloc中

现在虽然中断使能已经打开,在用户态进程运行的时候,外部中断会产生并进入内核,但是现在还没有能处理这类中断

所以需要修改trap_dispatch,在发生外部定时中断的时候,调用调度器,调度另外一个可运行的进程

在跑make run-spin的时候,总是遇到bug,发现父进程调用sys_env_destroy没能把子进程杀死

找了半天,发现传入sys_env_destroy的参数是子进程的进程id,而在内核的系统调用sys_env_destroy中,却莫名其妙的变成了父进程的进程id,最后发现是syscall函数写错了

这一句的参数应该是a1,我却写成了curenv->env_id

这个问题显然是自lab3就有的,但是到现在才发现是错的,而且之前一直没有出错。。。

实现IPC

IPC是计算机系统中非常重要的一部分,而JOS选择了一种看起来挺别扭的方法来实现

两个进程需要通信的话,一方要发起recv,然后阻塞,直到有一个进程调用send向正在接受的进程发送了信息,阻塞的进程才会被唤醒

而JOS中,可以允许传递两种信息,一是一个32位整数,另外一个就是传递页的映射,在这个过程中,接收方和发送方将同时映射到一个相同的物理页,此时也就实现了内存共享

然后将这两个功能实现在了同一个系统调用中

首先看内核中的sys_ipc_recv函数。当一个进程试图去接收信息的时候,应该将自己标记为正在接收信息,而且为了不浪费CPU资源,应该同时标记自己为ENV_NOT_RUNNABLE,只有当有进程向自己发了信息之后,才会重新恢复可运行

将自己标记为不可运行之后,调用调度器运行其他进程

而内核中的sys_ipc_try_send的实现则相对来说麻烦很多,因为有很多的检测项,包括权限是否符合要求,要传送的页有没有,能不能将这一页映射到对方页表中去等等

如果srcva是在UTOP之下,那么说明是要共享内存,那就首先要在发送方的页表中找到srcva对应的页表项,然后在接收方给定的虚地址处插入这个页表项

接收完成之后,重新将当前进程设置为可运行,同时把env_ipc_recving设置为0,以防止其他的进程再发送,覆盖掉当前的内容

以上的两个函数都是系统调用,而在用户态下的函数调用需要另外实现

至此,整个lab4就实现完成了,好累。。。

时间: 2024-09-30 04:23:15

MIT 6.828 JOS/XV6 LAB4-partC的相关文章

MIT 6.828 JOS/XV6 LAB4-partA

这一部分要实现的是对多核处理器的支持,然后实现系统调用只喜欢用户应用创建新的应用,而且还要实现round-robin调度算法 Multiprocessor support jos中对CPU进行了抽象 要描述一个CPU, 需要知道id,运行状态,当前正在运行的进程 所有的cpu数目放在cpus数组中 接下来则是对有多个cpu的处理器的抽象,这里使用了三个结构体,总之是比较乱,目前还不能完全看懂 多核处理器的初始化都在mp_init函数中完成,首先是调用mpconfig函数,主要功能是寻找一个MP

MIT 6.828 JOS/XV6 LAB4-partB

这里要实现的就是UNIX标准系统调用中的fork,核心当然是copy on write技术 至于为什么采用copy on write,是因为子进程在被创建之后很可能立刻执行exec()了,之前做的一系列的拷贝是无用功 所以说,当创建一个新的子进程的时候,只需要拷贝父进程的内存映射(页表)就可以了,而且将父进程所有的内存映射页都标记为只读的,这样,当子进程或者父进程尝试去读的时候是安全的,而当尝试去写的时候,就会出发page fault,而在page fault处理例程中,单独将被写入的页(比如说

MIT 6.828 JOS学习笔记2. Lab 1 Part 1.2: The kernel

Lab 1 Part 1: PC bootstrap 我们继续~ PC机的物理地址空间 这一节我们将深入的探究到底PC是如何启动的.首先我们看一下通常一个PC的物理地址空间是如何布局的:                           这张图仅仅展示了内存空间的一部分. 第一代PC处理器是16位字长的Intel 8088处理器,这类处理器只能访问1MB的地址空间,即0x00000000~0x000FFFFF.但是这1MB也不是用户都能利用到的,只有低640KB(0x00000000~0x00

MIT 6.828 JOS学习笔记17. Lab 3.1 Part A User Environments

Introduction 在这个实验中,我们将实现操作系统的一些基本功能,来实现用户环境下的进程的正常运行.你将会加强JOS内核的功能,为它增添一些重要的数据结构,用来记录用户进程环境的一些信息:创建一个单一的用户环境,并且加载一个程序运行它.你也可以让JOS内核能够完成用户环境所作出的任何系统调用,以及处理用户环境产生的各种异常. Part A: User Environments and Exception Handling 新包含的文件inc/env.h里面包含了JOS内核的有关用户环境(

MIT 6.828 JOS学习笔记7. Lab 1 Part 2.2: The Boot Loader

Lab 1 Part 2 The Boot Loader Loading the Kernel 我们现在可以进一步的讨论一下boot loader中的C语言的部分,即boot/main.c.但是在我们分析之前,我们应该先回顾一些关于C语言的基础知识. Exercise 4: 阅读关于C语言的指针部分的知识.最好的参考书自然是"The C Programming Language". 阅读5.1到5.5节.然后下载pointers.c的代码,并且编译运行它,确保你理解在屏幕上打印出来的所

MIT 6.828 JOS学习笔记16. Lab 2.2

Part 3 Kernel Address Space JOS把32位线性地址虚拟空间划分成两个部分.其中用户环境(进程运行环境)通常占据低地址的那部分,叫用户地址空间.而操作系统内核总是占据高地址的部分,叫内核地址空间.这两个部分的分界线是定义在memlayout.h文件中的一个宏 ULIM.JOS为内核保留了接近256MB的虚拟地址空间.这就可以理解了,为什么在实验1中要给操作系统设计一个高地址的地址空间.如果不这样做,用户环境的地址空间就不够了. Permission and Fault

MIT 6.828 JOS学习笔记5. Exercise 1.3

Exercise 1.3 设置一个断点在地址0x7c00处,这是boot sector被加载的位置.然后让程序继续运行直到这个断点.跟踪/boot/boot.S文件的每一条指令,同时使用boot.S文件和系统为你反汇编出来的文件obj/boot/boot.asm.你也可以使用GDB的x/i指令来获取去任意一个机器指令的反汇编指令,把源文件boot.S文件和boot.asm文件以及在GDB反汇编出来的指令进行比较. 追踪到bootmain函数中,而且还要具体追踪到readsect()子函数里面.找

MIT 6.828 JOS学习笔记4. Lab 1 Part 2.1: The Boot Loader

Part 2: The Boot Loader 对于PC来说,软盘,硬盘都可以被划分为一个个大小为512字节的区域,叫做扇区.一个扇区是一次磁盘操作的最小粒度.每一次读取或者写入操作都必须是一个或多个扇区.如果一个磁盘是可以被用来启动操作系统的,就把这个磁盘的第一个扇区叫做启动扇区.这一部分介绍的boot loader程序就位于这个启动扇区之中.当BIOS找到一个可以启动的软盘或硬盘后,它就会把这512字节的启动扇区加载到内存地址0x7c00~0x7dff这个区域内. 对于6.828,我们将采用

MIT 6.828 JOS学习笔记10. Lab 1 Part 3: The kernel

Lab 1 Part 3: The kernel 现在我们将开始具体讨论一下JOS内核了.就像boot loader一样,内核开始的时候也是一些汇编语句,用于设置一些东西,来保证C语言的程序能够正确的执行. 使用虚拟内存 在运行boot loader时,boot loader中的链接地址(虚拟地址)和加载地址(物理地址)是一样的.但是当进入到内核程序后,这两种地址就不再相同了. 操作系统内核程序在虚拟地址空间通常会被链接到一个非常高的虚拟地址空间处,比如0xf0100000,目的就是能够让处理器