adjust the formats of the labs

pull/1/head
Zhiyuan Shao 4 years ago
parent 1ed8cc61b8
commit 7908a91c8b

@ -1,6 +1,6 @@
## 第三章实验2系统调用的实现
# 第三章实验2系统调用的实现
### 3.1 实验环境搭建
## 3.1 实验环境搭建
实验2需要在实验1的基础之上完成所以首先你需要切换到lab2_small的branch然后commit你的代码。
@ -32,17 +32,13 @@ lab5_small
完成一切后,我们就可以正式进入实验二了!
### 3.2 实验内容
## 3.2 实验内容
实验要求:了解系统调用的执行过程,并实现一个自定义的系统调用。
**3.2.1 练习一在app中使用系统调用**
#### 应用: ####
系统调用的英文名字是System Call用户通过系统调用来执行一些需要特权的任务那么我们具体在app中是如何使用内核所提供的系统调用的呢
RISC-V中提供了ecall指令用于向运行时环境发出请求我们可以使用内联汇编使用该指令进行系统调用代码如下
app2_1.c源文件如下
```
1 #define ecall() ({\
2 asm volatile(\
3 "li x17,81\n"\
@ -54,23 +50,58 @@ RISC-V中提供了ecall指令用于向运行时环境发出请求我们可
9 ecall();
10 return 0;
11 }
```
例3.1 ecall
以上代码中我们将系统调用号81通过x17寄存器传给内核再通过ecall指令进行系统调用当然目前代理内核中并没有81号系统调用的实现这需要我们在后面的实验中完成。
**3.2.2 练习二:系统调用过程跟踪**
#### 任务一 : 在app中使用系统调用理解 ####
任务描述:
系统调用的英文名字是System Call用户通过系统调用来执行一些需要特权的任务那么我们具体在app中是如何使用内核所提供的系统调用的呢
RISC-V中提供了ecall指令用于向运行时环境发出请求我们可以使用内联汇编使用该指令进行系统调用。
预期输出:
理解app2_1的调用过程。
#### 任务二 : 系统调用过程跟踪(理解) ####
任务描述:
在我们执行了ecall指令后代理内核中又是如何执行的呢
在第一章的表1.7中我们可以看到Environment call from U-mode是exception(trap)的一种其对应的code是8。我们在实验二中已经讨论过中断入口函数位置的设置现在继续跟踪中断入口函数找出系统调用的执行过程。
在第一章的表1.7中我们可以看到Environment call from U-mode是exception(trap)的一种其对应的code是8。
预期输出:
我们在实验二中已经讨论过中断入口函数位置的设置,现在继续跟踪中断入口函数,找出系统调用的执行过程。
#### 任务三 : 自定义系统调用(编程) ####
任务描述:
**3.2.3 练习三:自定义系统调用(需要编程)**
阅读pk目录syscall.c文件增加一个系统调用sys_get_memsize()系统调用返回spike设置的内存空间大小, 系统调用号为81。
提示在pk目录下的mmap.c文件中函数pk_vm_init中定义了代理内核的内存空间大小。
预期输出:
在pk目录下的mmap.c文件中函数pk_vm_init中定义了代理内核的内存空间大小。
spike 通过-m来指定分配的物理内存单位是MiB默认为2048。如
@ -111,7 +142,7 @@ test3_m1024 : OK
Score: 20/20
```
### 3.3 基础知识
## 3.3 实验指导
**3.3.1 系统调用**
@ -236,9 +267,9 @@ Score: 20/20
221 }
```
在enter_supervisor_mode函数中将 mstatus的MPP域设置为1表示中断发生之前的模式是Supervisor将mstatus的MPIE域设置为0表示中断发生前MIE的值为0。随即将机器模式的内核栈顶写入mscratch寄存器中设置mepc为rest_of_boot_loader的地址并将kernel_stack_top与0作为参数存入a0和a1。
在enter_supervisor_mode函数中将 mstatus的MPP域设置为1表示中断发生之前的模式是Superior将mstatus的MPIE域设置为0表示中段发生前MIE的值为0。随机将机器模式的内核栈顶写入mscratch寄存器中设置mepc为rest_of_boot_loader的地址并将kernel_stack_top与0作为参数存入a0和a1。
最后执行mret指令该指令执行时程序从机器模式的异常返回将程序计数器pc设置为mepc即rest_of_boot_loader的地址将特权级设置为mstatus寄存器的MPP域即方才所设置的代表Supervisor的1MPP设置为0将mstatus寄存器的MIE域设置为MPIE即方才所设置的表示中断关闭的0MPIE设置为1。
最后执行mret指令该指令执行时程序从机器模式的异常返回将程序计数器pc设置为mepc即rest_of_boot_loader的地址将特权级设置为mstatus寄存器的MPP域即方才所设置的代表Superior的1MPP设置为0将mstatus寄存器的MIE域设置为MPIE即方才所设置的表示中断关闭的0MPIE设置为1。
于是当mret指令执行完毕程序将从rest_of_boot_loader继续执行。
@ -308,7 +339,7 @@ Score: 20/20
在61行交换了sp与sscratch的值这里是为了根据sscratch的值判断该中断是来源于U模式还是S模式。
如果sp也就是传入的sscratch值不为零则跳转至64行若sscratch的值为零则恢复原sp中的值。这是因为当中断来源于S模式sscratch的值为0sp中存储的就是内核的堆栈地址。而当中断来源于U模式时sp中存储的是用户的堆栈地址sscratch中存储的则是内核的堆栈地址需要交换二者是sp指向内核的堆栈地址。
如果sp也就是传入的sscratch值不为零则跳转至64行若sscratch的值为零则恢复原sp中的值。这是因为当中断来源于S模式sscratch的值为0sp中存储的就是内核的堆栈地址。而当中断来源于U模式时sp中存储的是用户的堆栈地址sscratch中存储的则是内核的堆栈地址需要交换二者是sp指向内核的堆栈地址。
接着在64,65行保存上下文最后跳转至67行处理trap。handle_trap在pk目录下的handlers.c文件中代码如下

@ -1,13 +1,87 @@
## 第四章实验3物理内存管理
# 第四章实验3物理内存管理
### 4.1 实验内容
## 4.1 实验内容
实验要求:了解物理内存,管理物理内存。
**4.1.1 练习一OS内存的初始化过程**
#### 应用: ####
app3_1.c源文件如下
1 #define ecall() ({\
2 asm volatile(\
3 "li x17,81\n"\
4 "ecall");\
5 })
6
7 int main(void){
8 //调用自定义的81号系统调用
9 ecall();
10 return 0;
11 }
对于操作系统来说内存分配的过程需要对应用层透明故而实验三的app同实验二相同并在内核中对于的内存分配单元做如下校验
static void
basic_check(void) {
struct Page *p0, *p1, *p2;
p0 = p1 = p2 = NULL;
assert((p0 = alloc_page()) != NULL);
assert((p1 = alloc_page()) != NULL);
assert((p2 = alloc_page()) != NULL);
assert(p0 != p1 && p0 != p2 && p1 != p2);
assert(p0->ref == 0 && p1->ref == 0 && p2->ref == 0);
list_entry_t free_list_store = free_list;
list_init(&free_list);
assert(list_empty(&free_list));
unsigned int nr_free_store = nr_free;
nr_free = 0;
free_page(p0);
free_page(p1);
free_page(p2);
assert(nr_free == 3);
assert((p0 = alloc_page()) != NULL);
assert((p1 = alloc_page()) != NULL);
assert((p2 = alloc_page()) != NULL);
assert(alloc_page() == NULL);
free_page(p0);
assert(!list_empty(&free_list));
struct Page *p;
assert((p = alloc_page()) == p0);
assert(alloc_page() == NULL);
assert(nr_free == 0);
free_list = free_list_store;
nr_free = nr_free_store;
free_page(p);
free_page(p1);
free_page(p2);
}
#### 任务一 : OS内存的初始化过程理解 ####
任务描述:
在"pk/mmap.c"内有 pk_vm_init()函数阅读该函数了解OS内存初始化的过程。
预期输出:
```
364 uintptr_t pk_vm_init()
365 {
@ -49,7 +123,11 @@
以上代码中我们给出了大体的注释请根据以上代码读者可以尝试画一下PK的逻辑地址空间结构图以及逻辑地址空间到物理地址空间的映射关系。
**4.1.2 练习二first_fit内存页分配算法需要编程**
#### 任务二 : first_fit内存页分配算法编程 ####
任务描述:
在"pk/pmm.c" 中,我们实现了对物理内存的管理。
@ -87,7 +165,11 @@ l pmm_check :检查校验函数
first_fit分配算法需要维护一个查找有序地址按从小到大排列空闲块以页为最小单位的连续地址空间的数据结构而双向链表是一个很好的选择。pk/list.h定义了可挂接任意元素的通用双向链表结构和对应的操作所以需要了解如何使用这个文件提供的各种函数从而可以完成对双向链表的初始化/插入/删除等。
预期输出:
我们在实验二中已经讨论过中断入口函数位置的设置,现在继续跟踪中断入口函数,找出系统调用的执行过程。
你可以使用python脚本检查你的输出
@ -104,7 +186,7 @@ running app3 m1024 : OK
Score: 20/20
```
### 4.2 基础知识
## 4.2 实验指导
**4.2.1 物理内存空间与编址**

@ -1,12 +1,63 @@
## 第五章实验4缺页异常的处理
# 第五章实验4缺页异常的处理
### 5.1 实验内容
## 5.1 实验内容
实验要求在APP里写递归程序其执行过程导致栈的不断增长。在代理内核中实现缺页中断的处理例程trap使其能够支撑递归程序的正确执行。
#### 应用: ####
**练习一:缺页中断实例的完善(需要编程)**
app4_1.c源文件如下
1 #include<stdio.h>
2
3 int main()
4 {
5
6 uintptr_t addr = 0x7f000000;
7 *(int *)(addr)=1;
8
9 uintptr_t addr1_same_page = 0x7f000010;
10 uintptr_t addr2_same_page = 0x7f000fff;
11 *(int *)(addr1_same_page)=2;
12 *(int *)(addr2_same_page)=3;
13
14 uintptr_t addr1_another_page = 0x7f001000;
15 uintptr_t addr2_another_page = 0x7f001ff0;
16 *(int *)(addr1_another_page)=4;
17 *(int *)(addr2_another_page)=5;
18
19
20 return 0;
21 }
以上代码中对地址0x7f000000进行访问将触发缺页异常。随后对同一页内的地址0x7f000010、0x7f000fff进行访问此时由于页0x7f000000已近完成映射故而不会发生异常。最后有对新的一页进行访问将再次引发缺页异常。
app4_2.c源文件如下
1 #include <stdio.h>
2 void fun(int num){
3 if(num==0){
4 return;
5 }
6 fun(num-1);
7 }
8 int main(){
9 int num=10000;
10 fun(num);
11 printf("end \n");
12 return 0;
13 }
以上代码中进行了大量递归,这将产生缺页。
#### 任务一 : 缺页中断实例的完善(编程) ####
任务描述:
**在**"pk/mmap.c"内有__handle_page_fault()函数,完善该函数,实现缺页中的处理。
@ -33,6 +84,10 @@
221 //<----------end
```
预期输出:
当你完成__handle_page_fault()函数后,可进行如下测试:
编译app目录下实验四相关文件
@ -74,7 +129,7 @@ running app4_2 : OK
test4_2 : OK
```
### 5.2 基础知识
## 5.2 基础知识
**5.2.1 虚拟地址空间**

@ -1,14 +1,32 @@
## 第六章实验5进程的封装
# 第六章实验5进程的封装
### 6.1 实验内容
## 6.1 实验内容
实验要求在APP里写fork调用其执行过程将fork出一个子进程。在代理内核中实现fork的处理例程trap使其能够支撑APP程序的正确执行。
#### 应用: ####
在本次实验的app4.c文件中将会测试fork函数。代码中170及172系统调用分别对应着sys_fork和sys_getpid系统调用。调用fork函数后将会有两个返回。在父进程中fork返回新创建子进程的进程ID而在子进程中fork返回0。你需要阅读proc.c文件完善相关代码是的app4.c可以正常运行。
app5.c源文件如下
int main(){
**6.1.1 练习一alloc_proc需要编程**
if(fork() == 0) {
printf("this is child process;my pid = %d\n",getpid());
}else {
printf("this is farther process;my pid = %d\n",getpid());
}
return 0;
}
以上代码中进行了fork调用其执行过程将fork出一个子进程。
#### 任务一 : alloc_proc编程 ####
任务描述:
完善"pk/proc.c"中的alloc_proc(),你需要对以下属性进行初始化:
@ -35,12 +53,16 @@ l uintptr_t pagetable;
l uint32_t flags;
l char name[PROC_NAME_LEN + 1];
```
**6.1.2 练习二do_fork需要编程**
#### 任务二 : do_fork编程 ####
任务描述:
完善"pk/proc.c"中的do_fork函数你需要进行以下工作
l 调用alloc_proc()来为子进程创建进程控制块
@ -58,6 +80,9 @@ l 将子进程加入到链表中
预期输出:
完成以上代码后,你可以进行如下测试,然后输入如下命令:
`$ riscv64-unknown-elf-gcc ../app/app5.c -o ../app/elf/app5`
@ -116,7 +141,7 @@ running app5 : OK
Score: 20/20
```
### 6.2 基础知识
## 6.2 实验指导
**6.2.1 进程结构**

Loading…
Cancel
Save