format titles
This commit is contained in:
parent
ba1480cf61
commit
0bba4377cf
|
@ -160,7 +160,7 @@
|
|||
* [项目组成](lab7/lab7_2_2_files.md)
|
||||
* [同步互斥的设计与实现](lab7/lab7_3_synchronization_implement.md)
|
||||
* [实验执行流程概述](lab7/lab7_3_1_experiment.md)
|
||||
* [计时器的原理和实现](lab6/lab7_3_2_timer_implement.md)
|
||||
* [计时器的原理和实现](lab7/lab7_3_2_timer_implement.md)
|
||||
* [同步互斥的底层支撑](lab7/lab7_3_2_synchronization_basic_support.md)
|
||||
* [信号量](lab7/lab7_3_3_semaphore.md)
|
||||
* [管程和条件变量](lab7/lab7_3_4_monitors.md)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
## 1.实验目的:
|
||||
## 实验目的:
|
||||
|
||||
- 了解操作系统开发实验环境
|
||||
- 熟悉命令行方式的编译、调试工程
|
||||
- 掌握基于硬件模拟器的调试技术
|
||||
- 熟悉C语言编程和指针的概念
|
||||
- 了解X86汇编语言
|
||||
- 了解X86汇编语言
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
### 2.1 了解OS实验
|
||||
### 了解OS实验
|
||||
写一个操作系统难吗?别被现在上百万行的Linux和Windows操作系统吓倒。当年Thompson乘他老婆带着小孩度假留他一人在家时,写了UNIX;当年Linus还是一个21岁大学生时完成了Linux雏形。站在这些巨人的肩膀上,我们能否也尝试一下做“巨人”的滋味呢?
|
||||
|
||||
MIT的Frans Kaashoek等在2006年参考PDP-11上的UNIX Version 6写了一个可在X86上跑的操作系统xv6(基于MIT License),用于学生学习操作系统。我们可以站在他们的肩膀上,基于xv6的设计,尝试着一步一步完成一个从“空空如也”到“五脏俱全”的“麻雀”操作系统—ucore,此“麻雀”包含虚存管理、进程管理、处理器调度、同步互斥、进程间通信、文件系统等主要内核功能,总的内核代码量(C+asm)不会超过5K行。充分体现了“小而全”的指导思想。
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#### 2.2.1 开发OS lab实验的简单步骤
|
||||
#### 开发OS lab实验的简单步骤
|
||||
|
||||
在某git server,比如 https://github.com/chyyuu/ucore_lab 可下载我们提供的lab1~lab8实验软件中,大致经过如下过程就可以完成使用。
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#### 2.2.2 通过虚拟机使用Linux实验环境(推荐:最容易的实验环境安装方法)
|
||||
#### 通过虚拟机使用Linux实验环境(推荐:最容易的实验环境安装方法)
|
||||
|
||||
这是最简单的一种通过虚拟机方式使用Linux并完成OS各个实验的方法,不需要安装Linux操作系统和各种实验所需开发软件。首先安装VirtualBox 虚拟机软件(有windows版本和其他OS版本,可到 http://www.virtualbox.org/wiki/Downloads 下载),然后在[百度云盘上](http://pan.baidu.com/s/11zjRK)下载一个已经安装好各种所需编辑/开发/调试/运行软件的Linux实验环境的VirtualBox虚拟硬盘文件(mooc-os-2015.vdi.xz,包含一个虚拟磁盘镜像文件和两个配置描述文件,下载此文件的网址址见https://github.com/chyyuu/ucore_lab下的README中的描述)。用2345好压软件(有windows版本,可到http://www.haozip.com 下载。一般软件解压不了xz格式的压缩文件)先解压到C盘的vms目录下即:
|
||||
C:\vms\mooc-os-2015.vdi
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
##### 2.2.3.1 实验中可能使用的软件
|
||||
##### 实验中可能使用的软件
|
||||
|
||||
***编辑器***
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#### 2.2.3 安装使用Linux实验环境(适合希望自己安装Linux系统的同学)
|
||||
#### 安装使用Linux实验环境(适合希望自己安装Linux系统的同学)
|
||||
|
||||
这里我们主要以Ubuntu Linux 14.04(64 bit)作为整个实验的系统软件环境。首先我们需要安装Ubuntu Linux 14.04。
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
### 2.2 设置实验环境
|
||||
### 设置实验环境
|
||||
|
||||
我们参考了MIT的xv6、Harvard的OS161和Linux等设计了ucore OS实验,所有OS实验需在Linux下运行。对于经验不足的同学,推荐参考“通过虚拟机使用Linux实验环境”一节用虚拟机方式进行试验。
|
||||
> 也有同学在MAC系统和Windows系统中搭建实验环境,不过过程相对比较复杂,这里就不展开介绍了。
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
###### 2.3.1.1 编译简单的 C 程序
|
||||
###### 编译简单的 C 程序
|
||||
|
||||
C 语言经典的入门例子是 Hello World,下面是一示例代码:
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
##### 2.3.1.2 AT&T汇编基本语法
|
||||
##### AT&T汇编基本语法
|
||||
|
||||
Ucore中用到的是AT&T格式的汇编,与Intel格式的汇编有一些不同。二者语法上主要有以下几个不同:
|
||||
```
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
##### 2.3.1.3 GCC基本内联汇编
|
||||
##### GCC基本内联汇编
|
||||
|
||||
GCC 提供了两内内联汇编语句(inline asm statements):基本内联汇编语句(basic inline asm statement)和扩展内联汇编语句(extended inline asm statement)。GCC基本内联汇编很简单,一般是按照下面的格式:
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
##### 2.3.1.4 GCC扩展内联汇编
|
||||
##### GCC扩展内联汇编
|
||||
|
||||
使用GCC扩展内联汇编的例子如下:
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
#### 2.3.1 gcc的基本用法
|
||||
#### gcc的基本用法
|
||||
|
||||
如果你还没装gcc编译环境或自己不确定装没装,不妨先执行 :
|
||||
|
||||
sudo apt-get install build-essential
|
||||
sudo apt-get install build-essential
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#### 2.3.2 make和Makefile
|
||||
#### make和Makefile
|
||||
|
||||
GNU make(简称make)是一种代码维护工具,在大中型项目中,它将根据程序各个模块的更新情况,自动的维护和生成目标代码。
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#### 2.3.3 gdb使用
|
||||
#### gdb使用
|
||||
|
||||
gdb 是功能强大的调试程序,可完成如下的调试任务:
|
||||
- 设置断点
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
|
||||
##### 2.3.4 进一步的相关内容
|
||||
##### 进一步的相关内容
|
||||
|
||||
请同学网上搜寻相关资料学习:
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
### 了解编程开发调试的基本工具
|
||||
|
||||
|
||||
### 2.3 了解编程开发调试的基本工具
|
||||
|
||||
在Ubuntu Linux中的C语言编程主要基于GNU C的语法,通过gcc来编译并生成最终执行文件。GNU汇编(assembler)采用的是AT&T汇编格式,Microsoft 汇编采用Intel格式。
|
||||
在Ubuntu Linux中的C语言编程主要基于GNU C的语法,通过gcc来编译并生成最终执行文件。GNU汇编(assembler)采用的是AT&T汇编格式,Microsoft 汇编采用Intel格式。
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
##### 2.4.1.1 Linux运行环境
|
||||
##### Linux运行环境
|
||||
|
||||
QEMU用于模拟一台x86计算机,让ucore能够运行在QEMU上。为了能够正确的编译和安装 qemu,尽量使用最新版本的qemu(http://wiki.qemu.org/Download),或者os ftp服务器上提供的qemu源码:qemu-1.1.0.tar.gz)。目前 qemu 能够支持最新的 gcc-4.x 编译器。例如:在 Ubuntu 12.04 系统中,默认得版本是 gcc-4.6.x (可以通过 gcc -v 或者 gcc --version 进行查看)。
|
||||
|
||||
|
@ -8,4 +7,4 @@ QEMU用于模拟一台x86计算机,让ucore能够运行在QEMU上。为了能
|
|||
sudo apt-get install qemu-system
|
||||
|
||||
也可采用下面描述的方法对qemu进行源码级安装。
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
###### 2.4.1.2.1 获得并应用修改
|
||||
###### 获得并应用修改
|
||||
|
||||
编译qemu还会用到的库文件有 libsdl1.2-dev 等。安装命令如下:
|
||||
|
||||
|
@ -21,4 +21,4 @@
|
|||
qemu.patch qemu
|
||||
chy@chyhome-PC:~$cd qemu
|
||||
chy@chyhome-PC:~$patch -p1 -u < ../qemu.patch
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
###### 2.4.1.2.2 配置、编译和安装
|
||||
###### 配置、编译和安装
|
||||
|
||||
编译以及安装 qemu 前需要使用 <qemu>(表示qemu解压缩路径)下面的 configure 脚本生成相应的配置文件等。而 configure 脚本有较多的参数可供选择,可以通过如下命令进行查看:
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
|
||||
##### 2.4.1.2 Linux环境下的源码级安装过程
|
||||
##### Linux环境下的源码级安装过程
|
||||
|
|
|
@ -1 +1 @@
|
|||
#### 2.4.1 安装硬件模拟器QEMU
|
||||
#### 安装硬件模拟器QEMU
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
##### 2.4.2.1 运行参数
|
||||
##### 运行参数
|
||||
|
||||
如果 qemu 使用的是默认 /usr/local/bin 安装路径,则在命令行中可以直接使用 qemu 命令运行程序。qemu 运行可以有多参数,格式如:
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
##### 2.4.2.2 常用调试命令
|
||||
##### 常用调试命令
|
||||
|
||||
qemu中monitor的常用命令:
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
#### 2.4.2 使用硬件模拟器QEMU
|
||||
#### 使用硬件模拟器QEMU
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 2.4.3 基于qemu内建模式调试ucore
|
||||
#### 基于qemu内建模式调试ucore
|
||||
|
||||
调试举例:调试 lab1,跟踪bootmain函数:
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
##### 2.4.4.1 编译可调试的目标文件
|
||||
##### 编译可调试的目标文件
|
||||
|
||||
为了使得编译出来的代码是能够被gdb这样的调试器调试,我们需要在使用gcc编译源文件的时候添加参数:"-g"。这样编译出来的目标文件中才会包含可以用于调试器进行调试的相关符号信息。
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
##### 2.4.4.2 ucore 代码编译
|
||||
##### ucore 代码编译
|
||||
|
||||
(1) 编译过程:在解压缩后的 ucore 源码包中使用 make 命令即可。例如 lab1中:
|
||||
```
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
##### 2.4.4.3 使用远程调试
|
||||
##### 使用远程调试
|
||||
|
||||
为了与qemu配合进行源代码级别的调试,需要先让qemu进入等待gdb调试器的接入并且还不能让qemu中的CPU执行,因此启动qemu的时候,我们需要使用参数-S –s这两个参数来做到这一点。在使用了前面提到的参数启动qemu之后,qemu中的CPU并不会马上开始执行,这时我们启动gdb,然后在gdb命令行界面下,使用下面的命令连接到qemu:
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
##### 2.4.4.4 使用gdb配置文件
|
||||
##### 使用gdb配置文件
|
||||
|
||||
在上面可以看到,为了进行源码级调试,需要输入较多的东西,很麻烦。为了方便,可以将这些命令存在脚本中,并让gdb在启动的时候自动载入。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
##### 2.4.4.5 加载调试目标
|
||||
##### 加载调试目标
|
||||
|
||||
在上面小节,我们提到为了能够让gdb识别变量的符号,我们必须给gdb载入符号表等信息。在进行gdb本地应用程序调试的时候,因为在指定了执行文件时就已经加载了文件中包含的调试信息,因此不用再使用gdb命令专门加载了。但是在使用qemu进行远程调试的时候,我们必须手动加载符号表,也就是在gdb中用file命令。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
##### 2.4.4.6 设定调试目标架构
|
||||
##### 设定调试目标架构
|
||||
|
||||
在调试的时候,我们也许需要调试不是i386保护模式的代码,比如8086实模式的代码,我们需要设定当前使用的架构:
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
#### 2.4.4 结合gdb和qemu源码级调试ucore
|
||||
#### 结合gdb和qemu源码级调试ucore
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
### 2.4 基于硬件模拟器实现源码级调试
|
||||
### 基于硬件模拟器实现源码级调试
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 2.5.1 Intel 80386运行模式
|
||||
#### Intel 80386运行模式
|
||||
|
||||
一般CPU只有一种运行模式,能够支持多个程序在各自独立的内存空间中并发执行,且有用户特权级和内核特权级的区分,让一般应用不能破坏操作系统内核和执行特权指令。80386处理器有四种运行模式:实模式、保护模式、SMM模式和虚拟8086模式。这里对涉及ucore的实模式、保护模式做一个简要介绍。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 2.5.2 Intel 80386内存架构
|
||||
#### Intel 80386内存架构
|
||||
|
||||
地址是访问内存空间的索引。一般而言,内存地址有两个:一个是CPU通过总线访问物理内存用到的物理地址,一个是我们编写的应用程序所用到的逻辑地址(也有人称为虚拟地址)。比如如下C代码片段:
|
||||
```
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 2.5.3 Intel 80386寄存器
|
||||
#### Intel 80386寄存器
|
||||
|
||||
这里假定读者对80386 CPU有一定的了解,所以只作简单介绍。80386的寄存器可以分为8组:通用寄存器,段寄存器,指令指针寄存器,标志寄存器,系统地址寄存器,控制寄存器,调试寄存器,测试寄存器,它们的宽度都是32位。一般程序员看到的寄存器包括通用寄存器,段寄存器,指令指针寄存器,标志寄存器。
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
### 2.5 了解处理器硬件
|
||||
### 了解处理器硬件
|
||||
|
||||
要想深入理解ucore,就需要了解支撑ucore运行的硬件环境,即了解处理器体系结构(了解硬件对ucore带来影响)和机器指令集(读懂ucore的汇编)。ucore目前支持的硬件环境是基于Intel 80386以上的计算机系统。更多的硬件相关内容(比如保护模式等)将随着实现ucore的过程逐渐展开介绍。
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 2.6.1 面向对象编程方法
|
||||
#### 面向对象编程方法
|
||||
|
||||
uCore设计中采用了一定的面向对象编程方法。虽然C 语言对面向对象编程并没有原生支持,但没有原生支持并不等于我们不能用 C 语言写面向对象程序。需要注意,我们并不需要用 C语言模拟出一个常见 C++ 编译器已经实现的对象模型。如果是这样,还不如直接采用C++编程。
|
||||
|
||||
|
@ -29,4 +28,4 @@ uCore的面向对象编程方法,目前主要是采用了类似C++的接口(
|
|||
void (*check)(void);
|
||||
};
|
||||
这样基于此数据结构,我们可以实现不同连续内存分配算法的物理内存管理子系统,而这些物理内存管理子系统需要编写算法,把算法实现在此结构中定义的init(初始化)、init_memmap(分析空闲物理内存并初始化管理)、alloc_pages(分配物理页)、free_pages(释放物理页)函数指针所对应的函数中。而其他内存子系统需要与物理内存管理子系统交互时,只需调用特定物理内存管理子系统所采用的pmm_manager数据结构变量中的函数指针即可
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
##### 2.6.2.1 双向循环链表
|
||||
##### 双向循环链表
|
||||
|
||||
在“数据结构”课程中,如果创建某种数据结构的双循环链表,通常采用的办法是在这个数据结构的类型定义中有专门的成员变量 data, 并且加入两个指向该类型的指针next和prev。例如:
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
#### 2.6.2 通用数据结构
|
||||
#### 通用数据结构
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
### 2.6 了解ucore编程方法和通用数据结构
|
||||
### 了解ucore编程方法和通用数据结构
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
|
||||
|
||||
## 2.准备知识
|
||||
## 准备知识
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
## 1.实验目的:
|
||||
## 实验目的:
|
||||
|
||||
操作系统是一个软件,也需要通过某种机制加载并运行它。在这里我们将通过另外一个更加简单的软件-bootloader来完成这些工作。为此,我们需要完成一个能够切换到x86的保护模式并显示字符的bootloader,为启动操作系统ucore做准备。lab1提供了一个非常小的bootloader和ucore OS,整个bootloader执行代码小于512个字节,这样才能放到硬盘的主引导扇区中。通过分析和实现这个bootloader和ucore OS,读者可以了解到:
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#### 练习3:分析bootloader进入保护模式的过程。(要求在报告中写出分析)
|
||||
|
||||
BIOS将通过读取硬盘主引导扇区到内存,并转跳到对应内存中的位置执行bootloader。请分析bootloader是如何完成从实模式进入保护模式的。
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
#### 练习4:分析bootloader加载ELF格式的OS的过程。(要求在报告中写出分析)
|
||||
|
||||
通过阅读bootmain.c,了解bootloader如何加载ELF文件。通过分析源代码和通过qemu来运行并调试bootloader&OS,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
### 2.1 练习
|
||||
### 练习
|
||||
为了实现lab1的目标,lab1提供了6个基本练习和1个扩展练习,要求完成实验报告。
|
||||
|
||||
对实验报告的要求:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 2.2 项目组成
|
||||
### 项目组成
|
||||
|
||||
lab1的整体目录结构如下所示:
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
## 2.实验内容:
|
||||
## 实验内容:
|
||||
|
||||
lab1中包含一个bootloader和一个OS。这个bootloader可以切换到X86保护模式,能够读磁盘并加载ELF执行文件格式,并显示字符。而这lab1中的OS只是一个可以处理时钟中断和显示字符的幼儿园级别OS。
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.1 BIOS启动过程
|
||||
### BIOS启动过程
|
||||
|
||||
当计算机加电后,一般不直接执行操作系统,而是执行系统初始化软件完成基本IO初始化和引导加载功能。简单地说,系统初始化软件就是在操作系统内核运行之前运行的一段小软件。通过这段小软件,我们可以初始化硬件设备、建立系统的内存空间映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。最终引导加载程序把操作系统内核映像加载到RAM中,并将系统控制权传递给它。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 3.2.1 保护模式和分段机制
|
||||
#### 保护模式和分段机制
|
||||
|
||||
为何要了解Intel 80386的保护模式和分段机制?首先,我们知道Intel 80386只有在进入保护模式后,才能充分发挥其强大的功能,提供更好的保护机制和更大的寻址空间,否则仅仅是一个快速的8086而已。没有一定的保护机制,任何一个应用软件都可以任意访问所有的计算机资源,这样也就无从谈起操作系统设计了。且Intel 80386的分段机制一直存在,无法屏蔽或避免。其次,在我们的bootloader设计中,涉及到了从实模式到保护模式的处理,我们的操作系统功能(比如分页机制)是建立在Intel 80386的保护模式上来设计的。如果我们不了解保护模式和分段机制,则我们面向Intel 80386体系结构的操作系统设计实际上是建立在一个空中楼阁之上。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 3.2.2 地址空间
|
||||
#### 地址空间
|
||||
|
||||
分段机制涉及4个关键内容:逻辑地址(Logical Address,应用程序员看到的地址,在操作系统原理上称为虚拟地址,以后提到虚拟地址就是指逻辑地址)、物理地址(Physical Address, 实际的物理内存地址)、段描述符表(包含多个段描述符的“数组”)、段描述符(描述段的属性,及段描述符表这个“数组”中的“数组元素”)、段选择子(即段寄存器中的值,用于定位段描述符表中段描述符表项的索引)
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 3.2.3 硬盘访问概述
|
||||
#### 硬盘访问概述
|
||||
|
||||
bootloader让CPU进入保护模式后,下一步的工作就是从硬盘上加载并运行OS。考虑到实现的简单性,bootloader的访问硬盘都是LBA模式的PIO(Program IO)方式,即所有的IO操作是通过CPU访问硬盘的IO地址寄存器完成。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 3.2.4 ELF文件格式概述
|
||||
#### ELF文件格式概述
|
||||
|
||||
ELF(Executable and linking format)文件格式是Linux系统下的一种常用目标文件(object file)格式,有三种主要类型:
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.2 bootloader启动过程
|
||||
### bootloader启动过程
|
||||
|
||||
BIOS将通过读取硬盘主引导扇区到内存,并转跳到对应内存中的位置执行bootloader。bootloader完成的工作包括:
|
||||
- 切换到保护模式,启用分段机制
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 3.3.1 函数堆栈
|
||||
#### 函数堆栈
|
||||
|
||||
栈是一个很重要的编程概念(编译课和程序设计课都讲过相关内容),与编译器和编程语言有紧密的联系。理解调用栈最重要的两点是:栈的结构,EBP寄存器的作用。一个函数调用动作可分解为:零到多个PUSH指令(用于参数入栈),一个CALL指令。CALL指令内部其实还暗含了一个将返回地址(即CALL指令下一条指令的地址)压栈的动作(由硬件完成)。几乎所有本地编译器都会在每个函数体之前插入类似如下的汇编指令:
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 3.3.2 中断与异常
|
||||
#### 中断与异常
|
||||
|
||||
操作系统需要对计算机系统中的各种外设进行管理,这就需要CPU和外设能够相互通信才行。一般外设的速度远慢于CPU的速度。如果让操作系统通过CPU“主动关心”外设的事件,即采用通常的轮询(polling)机制,则太浪费CPU资源了。所以需要操作系统和CPU能够一起提供某种机制,让外设在需要操作系统处理外设相关事件的时候,能够“主动通知”操作系统,即打断操作系统和应用的正常执行,让操作系统完成外设的相关处理,然后在恢复操作系统和应用的正常执行。在操作系统中,这种机制称为中断机制。中断机制给操作系统提供了处理意外情况的能力,同时它也是实现进程/线程抢占式调度的一个重要基石。但中断的引入导致了对操作系统的理解更加困难。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
#### 3.3.3 lab1中对中断的处理实现
|
||||
#### lab1中对中断的处理实现
|
||||
|
||||
(1) 外设基本初始化设置
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.3 操作系统启动过程
|
||||
### 操作系统启动过程
|
||||
|
||||
当bootloader通过读取硬盘扇区把ucore在系统加载到内存后,就转跳到ucore操作系统在内存中的入口位置(kern/init.c中的kern_init函数的起始地址),这样ucore就接管了整个控制权。当前的ucore功能很简单,只完成基本的内存管理和外设中断管理。ucore主要完成的工作包括:
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
## 3 从机器启动到操作系统运行的过程
|
||||
## 从机器启动到操作系统运行的过程
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
## 4 实验报告要求
|
||||
## 实验报告要求
|
||||
|
||||
从git server网站上取得ucore_lab后,进入目录labcodes/lab1,完成实验要求的各个练习。在实验报告中回答所有练习中提出的问题。在目录labcodes/lab1下存放实验报告,实验报告文档命名为lab1-学堂在线ID.md。推荐用**markdown**格式。对于lab1中编程任务,完成编写之后,再通过git push命令把代码同步回git server网站。最后请一定提前或按时提交到git server网站。
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
### 练习
|
||||
|
||||
对实验报告的要求:
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
### 项目组成
|
||||
|
||||
表1:实验三文件列表
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
## 实验内容
|
||||
|
||||
本次实验是在实验二的基础上,借助于页表机制和实验一中涉及的中断异常处理机制,完成Page
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
### 基本原理概述
|
||||
|
||||
什么是虚拟内存?简单地说是指程序员或CPU“看到”的内存。但有几点需要注意:
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
### 实验执行流程概述
|
||||
|
||||
本次实验主要完成ucore内核对虚拟内存的管理工作。其总体设计思路还是比较简单,即首先完成初始化虚拟内存管理机制,即需要设置好哪些页需要放在物理内存中,哪些页不需要放在物理内存中,而是可被换出到硬盘上,并涉及完善建立页表映射、页访问异常处理操作等函数实现。然后就执行一组访存测试,看看我们建立的页表项是否能够正确完成虚实地址映射,是否正确描述了虚拟内存页在物理内存中还是在硬盘上,是否能够正确把虚拟内存页在物理内存和硬盘之间进行传递,是否正确实现了页面替换算法等。lab3的总体执行流程如下。
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.3 关键数据结构和相关函数分析
|
||||
### 关键数据结构和相关函数分析
|
||||
|
||||
对于第一个问题的出现,在于实验二中有关内存的数据结构和相关操作都是直接针对实际存在的资源--物理内存空间的管理,没有从一般应用程序对内存的“需求”考虑,即需要有相关的数据结构和操作来体现一般应用程序对虚拟内存的“需求”。一般应用程序的对虚拟内存的“需求”与物理内存空间的“供给”没有直接的对应关系,ucore是通过page
|
||||
fault异常处理来间接完成这二者之间的衔接。
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
## 虚拟内存管理
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
## Page Fault异常处理
|
||||
|
||||
实现虚存管理的一个关键是page fault异常处理,其过程中主要涉及到函数 -- do\_pgfault的具体实现。比如,在程序的执行过程中由于某种原因(页框不存在/写只读页等)而使 CPU 无法最终访问到相应的物理内存单元,即无法完成从虚拟地址到物理地址映射时,CPU 会产生一次页访问异常,从而需要进行相应的页访问异常的中断服务例程。这个页访问异常处理的时机被操作系统充分利用来完成虚存管理,即实现“按需调页”/“页换入换出”处理的执行时机。当相关处理完成后,页访问异常服务例程会返回到产生异常的指令处重新执行,使得应用软件可以继续正常运行下去。
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
### 5.1 页替换算法
|
||||
### 页替换算法
|
||||
|
||||
操作系统为何要进行页面置换呢?这是由于操作系统给用户态的应用程序提供了一个虚拟的“大容量”内存空间,而实际的物理内存空间又没有那么大。所以操作系统就就“瞒着”应用程序,只把应用程序中“常用”的数据和代码放在物理内存中,而不常用的数据和代码放在了硬盘这样的存储介质上。如果应用程序访问的是“常用”的数据和代码,那么操作系统已经放置在内存中了,不会出现什么问题。但当应用程序访问它认为应该在内存中的的数据或代码时,如果这些数据或代码不在内存中,则根据上一小节的介绍,会产生页访问异常。这时,操作系统必须能够应对这种页访问异常,即尽快把应用程序当前需要的数据或代码放到内存中来,然后重新执行应用程序产生异常的访存指令。如果在把硬盘中对应的数据或代码调入内存前,操作系统发现物理内存已经没有空闲空间了,这时操作系统必须把它认为“不常用”的页换出到磁盘上去,以腾出内存空闲空间给应用程序所需的数据或代码。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 5.2 页面置换机制
|
||||
### 页面置换机制
|
||||
|
||||
如果要实现页面置换机制,只考虑页替换算法的设计与实现是远远不够的,还需考虑其他问题:
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
## 5. 页面置换机制的实现
|
||||
## 页面置换机制的实现
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
## 6. 实验报告要求
|
||||
## 实验报告要求
|
||||
|
||||
从git server网站上取得ucore_lab后,进入目录labcodes/lab3,完成实验要求的各个练习。在实验报告中回答所有练习中提出的问题。在目录labcodes/lab3下存放实验报告,实验报告文档命名为lab3-学生ID.md。推荐用**markdown**格式。对于lab3中编程任务,完成编写之后,再通过git push命令把代码同步回git server网站。最后请一定提前或按时提交到git server网站。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
## 1. 实验目的
|
||||
## 实验目的
|
||||
|
||||
* 了解内核线程创建/执行的管理过程
|
||||
* 了解内核线程的切换和基本调度过程
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 2.1 练习
|
||||
### 练习
|
||||
|
||||
对实验报告的要求:
|
||||
- 基于markdown格式来完成,以文本方式为主
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 2.2 项目组成
|
||||
### 项目组成
|
||||
|
||||
```
|
||||
├── boot
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
## 2. 实验内容
|
||||
## 实验内容
|
||||
|
||||
实验2/3完成了物理和虚拟内存管理,这给创建内核线程(内核线程是一种特殊的进程)打下了提供内存管理的基础。当一个程序加载到内存中运行时,首先通过ucore OS的内存管理子系统分配合适的空间,然后就需要考虑如何分时使用CPU来“并发”执行多个程序,让每个运行的程序(这里用线程或进程表示)“感到”它们各自拥有“自己”的CPU。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.1 实验执行流程概述
|
||||
### 实验执行流程概述
|
||||
|
||||
lab2和lab3完成了对内存的虚拟化,但整个控制流还是一条线串行执行。lab4将在此基础上进行CPU的虚拟化,即让ucore实现分时共享CPU,实现多条控制流能够并发执行。从某种程度上,我们可以把控制流看作是一个内核线程。本次实验将首先接触的是内核线程的管理。内核线程是一种特殊的进程,内核线程与用户进程的区别有两个:内核线程只运行在内核态而用户进程会在在用户态和内核态交替运行;所有内核线程直接使用共同的ucore内核内存空间,不需为每个内核线程维护单独的内存空间而用户进程需要维护各自的用户内存空间。从内存空间占用情况这个角度上看,我们可以把线程看作是一种共享内存空间的轻量级进程。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.2 设计关键数据结构 -- 进程控制块
|
||||
### 设计关键数据结构 -- 进程控制块
|
||||
|
||||
在实验四中,进程管理信息用struct
|
||||
proc\_struct表示,在*kern/process/proc.h*中定义如下:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#### 1. 创建第 0 个内核线程 idleproc
|
||||
#### 创建第 0 个内核线程 idleproc
|
||||
|
||||
在init.c::kern\_init函数调用了proc.c::proc\_init函数。proc\_init函数启动了创建内核线程的步骤。首先当前的执行上下文(从kern\_init 启动至今)就可以看成是uCore内核(也可看做是内核进程)中的一个内核线程的上下文。为此,uCore通过给当前执行的上下文分配一个进程控制块以及对它进行相应初始化,将其打造成第0个内核线程 -- idleproc。具体步骤如下:
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#### 2. 创建第 1 个内核线程 initproc
|
||||
#### 创建第 1 个内核线程 initproc
|
||||
|
||||
第0个内核线程主要工作是完成内核中各个子系统的初始化,然后就通过执行cpu\_idle函数开始过退休生活了。所以uCore接下来还需创建其他进程来完成各种工作,但idleproc内核子线程自己不想做,于是就通过调用kernel\_thread函数创建了一个内核线程init\_main。在实验四中,这个子内核线程的工作就是输出一些字符串,然后就返回了(参看init\_main函数)。但在后续的实验中,init\_main的工作就是创建特定的其他内核线程或用户进程(实验五涉及)。下面我们来分析一下创建内核线程的函数kernel\_thread:
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#### 3. 调度并执行内核线程 initproc
|
||||
#### 调度并执行内核线程 initproc
|
||||
|
||||
在uCore执行完proc\_init函数后,就创建好了两个内核线程:idleproc和initproc,这时uCore当前的执行现场就是idleproc,等到执行到init函数的最后一个函数cpu\_idle之前,uCore的所有初始化工作就结束了,idleproc将通过执行cpu\_idle函数让出CPU,给其它内核线程执行,具体过程如下:
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.3 创建并执行内核线程
|
||||
### 创建并执行内核线程
|
||||
|
||||
建立进程控制块(proc.c中的alloc\_proc函数)后,现在就可以通过进程控制块来创建具体的进程/线程了。首先,考虑最简单的内核线程,它通常只是内核中的一小段代码或者函数,没有自己的“专属”空间。这是由于在uCore OS启动后,已经对整个内核内存空间进行了管理,通过设置页表建立了内核虚拟空间(即boot\_cr3指向的二级页表描述的空间)。所以uCore OS内核中的所有线程都不需要再建立各自的页表,只需共享这个内核虚拟空间就可以访问整个物理内存了。从这个角度看,内核线程被uCore OS内核这个大“内核进程”所管理。
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
## 3. 内核线程管理
|
||||
## 内核线程管理
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
## 4. 实验报告要求
|
||||
## 实验报告要求
|
||||
|
||||
从git server网站上取得ucore_lab后,进入目录labcodes/lab4,完成实验要求的各个练习。在实验报告中回答所有练习中提出的问题。在目录labcodes/lab4下存放实验报告,实验报告文档命名为lab4-学生ID.md。推荐用**markdown**格式。对于lab4中编程任务,完成编写之后,再通过git push命令把代码同步回git server网站。最后请一定提前或按时提交到git server网站。
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
## 附录A:实验四的参考输出如下:
|
||||
|
||||
```
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
## 附录B:【原理】进程的属性与特征解析
|
||||
|
||||
操作系统负责进程管理,即从程序加载到运行结束的全过程,这个程序运行过程将经历从“出生”到“死亡”的完整“生命”历程。所谓“进程”就是指这个程序运行的整个执行过程。为了记录、描述和管理程序执行的动态变化过程,需要有一个数据结构,这就是进程控制块。进程与进程控制块是一一对应的。为此,ucore需要建立合适的进程控制块数据结构,并基于进程控制块来完成对进程的管理。
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
## 1. 实验目的
|
||||
## 实验目的
|
||||
|
||||
* 了解第一个用户进程创建过程
|
||||
* 了解系统调用框架的实现机制
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 2.1 练习
|
||||
### 练习
|
||||
|
||||
对实验报告的要求:
|
||||
- 基于markdown格式来完成,以文本方式为主
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 2.2 项目组成
|
||||
### 项目组成
|
||||
|
||||
```
|
||||
├── boot
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
## 2. 实验内容
|
||||
## 实验内容
|
||||
|
||||
实验4完成了内核线程,但到目前为止,所有的运行都在内核态执行。实验5将创建用户进程,让用户进程在用户态执行,且在需要ucore支持时,可通过系统调用来让ucore提供服务。为此需要构造出第一个用户进程,并通过系统调用sys\_fork/sys\_exec/sys\_exit/sys\_wait来支持运行不同的应用程序,完成对用户进程的执行过程的基本管理。相关原理介绍可看附录B。
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.1 实验执行流程概述
|
||||
### 实验执行流程概述
|
||||
|
||||
到实验四为止,ucore还一直在核心态“打转”,没有到用户态执行。提供各种操作系统功能的内核线程只能在CPU核心态运行是操作系统自身的要求,操作系统就要呆在核心态,才能管理整个计算机系统。但应用程序员也需要编写各种应用软件,且要在计算机系统上运行。如果把这些应用软件都作为内核线程来执行,那系统的安全性就无法得到保证了。所以,ucore要提供用户态进程的创建和执行机制,给应用程序执行提供一个用户态运行环境。接下来我们就简要分析本实验的执行过程,以及分析用户进程的整个生命周期来阐述用户进程管理的设计与实现。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.2 创建用户进程
|
||||
### 创建用户进程
|
||||
|
||||
在实验四中,我们已经完成了对内核线程的创建,但与用户进程的创建过程相比,创建内核线程的过程还远远不够。而这两个创建过程的差异本质上就是用户进程和内核线程的差异决定的。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.3 进程退出和等待进程
|
||||
### 进程退出和等待进程
|
||||
|
||||
当进程执行完它的工作后,就需要执行退出操作,释放进程占用的资源。ucore分了两步来完成这个工作,首先由进程本身完成大部分资源的占用内存回收工作,然后由此进程的父进程完成剩余资源占用内存的回收工作。为何不让进程本身完成所有的资源回收工作呢?这是因为进程要执行回收操作,就表明此进程还存在,还在执行指令,这就需要内核栈的空间不能释放,且表示进程存在的进程控制块不能释放。所以需要父进程来帮忙释放子进程无法完成的这两个资源回收工作。
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 3.4 系统调用实现
|
||||
### 系统调用实现
|
||||
|
||||
系统调用的英文名字是System
|
||||
Call。操作系统为什么需要实现系统调用呢?其实这是实现了用户进程后,自然引申出来需要实现的操作系统功能。用户进程只能在操作系统给它圈定好的“用户环境”中执行,但“用户环境”限制了用户进程能够执行的指令,即用户进程只能执行一般的指令,无法执行特权指令。如果用户进程想执行一些需要特权指令的任务,比如通过网卡发网络包等,只能让操作系统来代劳了。于是就需要一种机制来确保用户进程不能执行特权指令,但能够请操作系统“帮忙”完成需要特权指令的任务,这种机制就是系统调用。
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
## 3 用户进程管理
|
||||
## 用户进程管理
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
## 4. 实验报告要求
|
||||
## 实验报告要求
|
||||
|
||||
从git server网站上取得ucore_lab后,进入目录labcodes/lab5,完成实验要求的各个练习。在实验报告中回答所有练习中提出的问题。在目录labcodes/lab5下存放实验报告,实验报告文档命名为lab5-学生ID.md。推荐用**markdown**格式。对于lab5中编程任务,完成编写之后,再通过git push命令把代码同步回git server网站。最后请一定提前或按时提交到git server网站。
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
## 附录 A:【原理】用户进程的特征
|
||||
|
||||
### 从内核线程到用户进程
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
## 1 实验目的
|
||||
## 实验目的
|
||||
|
||||
* 理解操作系统的调度管理机制
|
||||
* 熟悉 ucore 的系统调度器框架,以及缺省的Round-Robin 调度算法
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
### 2.1 练习
|
||||
### 练习
|
||||
|
||||
对实验报告的要求:
|
||||
- 基于markdown格式来完成,以文本方式为主
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue