|
|
|
@ -2,7 +2,10 @@
|
|
|
|
|
|
|
|
|
|
> 扩充proxy kernel的代码,使你的内核可以支持以下应用程序:
|
|
|
|
|
|
|
|
|
|
## 应用程序一: ##
|
|
|
|
|
## 实验一 多进程支持 ##
|
|
|
|
|
|
|
|
|
|
### 应用: ###
|
|
|
|
|
|
|
|
|
|
App5_1的代码如下:
|
|
|
|
|
|
|
|
|
|
int main(){
|
|
|
|
@ -35,14 +38,79 @@ App5_1的代码如下:
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
### 实验一任务: ###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 任务一 : proc_pagetable/ vmcopy ####
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
实验任务描述:
|
|
|
|
|
|
|
|
|
|
实现pk/proc.c中的proc_pagetable与vmcopy函数,为进程创建用户页表,并映射用户内存,确保你的代码仍可以保证app5的正确运行。
|
|
|
|
|
|
|
|
|
|
以上是一段多进程的代码,我们可以结合下图进行分析:</br>
|
|
|
|
|
实验预期输出:
|
|
|
|
|
|
|
|
|
|
</br>
|
|
|
|
|
$ spike obj/pke app/elf/app5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
得到输出:
|
|
|
|
|
|
|
|
|
|
PKE IS RUNNING
|
|
|
|
|
to host 10
|
|
|
|
|
from host 0
|
|
|
|
|
elf name app/elf/app5
|
|
|
|
|
sched class: RR_scheduler
|
|
|
|
|
++ setup timer interrupts
|
|
|
|
|
log: proc init
|
|
|
|
|
father process
|
|
|
|
|
this is father process;my pid = 1
|
|
|
|
|
this is child process;my pid = 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 任务二:do_wait ####
|
|
|
|
|
|
|
|
|
|
实验任务描述:
|
|
|
|
|
实现`do_wait`函数,支持app5_1.c的运行
|
|
|
|
|
|
|
|
|
|
$ spike obj/pke app/elf/app5_1
|
|
|
|
|
预期得到输出:
|
|
|
|
|
|
|
|
|
|
PKE IS RUNNING
|
|
|
|
|
to host 10
|
|
|
|
|
from host 0
|
|
|
|
|
elf name app/elf/app5_1
|
|
|
|
|
sched class: RR_scheduler
|
|
|
|
|
++ setup timer interrupts
|
|
|
|
|
log: proc init
|
|
|
|
|
print proc app5_1
|
|
|
|
|
print proc this is child process;my pid = 2
|
|
|
|
|
print proc a=9
|
|
|
|
|
print proc this is child process;my pid = 3
|
|
|
|
|
print proc a=6
|
|
|
|
|
print proc this is farther process;my pid = 2
|
|
|
|
|
print proc a=5
|
|
|
|
|
print proc this is farther process;my pid = 1
|
|
|
|
|
print proc a=8
|
|
|
|
|
print proc this is child process;my pid = 4
|
|
|
|
|
print proc a=5
|
|
|
|
|
print proc this is farther process;my pid = 1
|
|
|
|
|
|
|
|
|
|
此时运行测试脚本:
|
|
|
|
|
|
|
|
|
|
$ python3 ./pke-final-1
|
|
|
|
|
|
|
|
|
|
预期得到输出:
|
|
|
|
|
|
|
|
|
|
build pk : OK
|
|
|
|
|
running app5_1 : OK
|
|
|
|
|
test fork : OK
|
|
|
|
|
Score: 20/20
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 实验一提示: ###
|
|
|
|
|
|
|
|
|
|
app5_1.c是一段多进程的代码,我们可以结合下图进行分析:</br>
|
|
|
|
|
<div style="text-align: center; width: 1000px; ">
|
|
|
|
|
<img src="lab_figures/final/app5_1.png" alt="app5_1" style="margin: 0 auto;" />
|
|
|
|
|
</div>
|
|
|
|
@ -265,25 +333,6 @@ start_user中首先恢复sstatus以及sepc,然后加载32个通用寄存器,
|
|
|
|
|
- 其四、该虚拟地址对应着用户栈,由于我们在proc_pagetable函数中已近为其分配了内存,此时只需要复制父进程的用户栈。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 任务一:proc_pagetable/ vmcopy ###
|
|
|
|
|
任务描述:实现pk/proc.c中的proc_pagetable与vmcopy函数,为进程创建用户页表,并映射用户内存,确保你的代码仍可以保证app5的正确运行。
|
|
|
|
|
|
|
|
|
|
$ spike obj/pke app/elf/app5
|
|
|
|
|
|
|
|
|
|
预期得到输出:
|
|
|
|
|
|
|
|
|
|
PKE IS RUNNING
|
|
|
|
|
to host 10
|
|
|
|
|
from host 0
|
|
|
|
|
elf name app/elf/app5
|
|
|
|
|
sched class: RR_scheduler
|
|
|
|
|
++ setup timer interrupts
|
|
|
|
|
log: proc init
|
|
|
|
|
father process
|
|
|
|
|
this is father process;my pid = 1
|
|
|
|
|
this is child process;my pid = 2
|
|
|
|
|
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
至此,我们进一步完善了对fork的支持。接下来,我们来看进程间的同步。</br>
|
|
|
|
@ -369,48 +418,17 @@ Schedule会选取下一个进程,随即进入switch_to,这又是一段汇编
|
|
|
|
|
|
|
|
|
|
当父进程被唤醒后,进入`wakeup_proc`的代码,在`wakeup_proc`函数中恢复了父进程的运行状态,随即再次调用schedule函数。如上文所述,schedule再次加载父进程的上下文,还记的方才父进程上下文中的ra寄存器的值吗?父进程将根据此值返回schedule中`proc_run`的下一行地址继续上次的代码,并返回schedule的调用函数`do_wai`t。`do_wait`里我们将再次查看子进程的state,若为PROC_ZOMBIE则返回。至此wait系统调用执行完毕。
|
|
|
|
|
|
|
|
|
|
### 任务二:do_wait ###
|
|
|
|
|
|
|
|
|
|
任务描述:实现`do_wait`函数,支持app5_1.c的运行
|
|
|
|
|
|
|
|
|
|
$ spike obj/pke app/elf/app5_1
|
|
|
|
|
预期得到输出:
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
PKE IS RUNNING
|
|
|
|
|
to host 10
|
|
|
|
|
from host 0
|
|
|
|
|
elf name app/elf/app5_1
|
|
|
|
|
sched class: RR_scheduler
|
|
|
|
|
++ setup timer interrupts
|
|
|
|
|
log: proc init
|
|
|
|
|
print proc app5_1
|
|
|
|
|
print proc this is child process;my pid = 2
|
|
|
|
|
print proc a=9
|
|
|
|
|
print proc this is child process;my pid = 3
|
|
|
|
|
print proc a=6
|
|
|
|
|
print proc this is farther process;my pid = 2
|
|
|
|
|
print proc a=5
|
|
|
|
|
print proc this is farther process;my pid = 1
|
|
|
|
|
print proc a=8
|
|
|
|
|
print proc this is child process;my pid = 4
|
|
|
|
|
print proc a=5
|
|
|
|
|
print proc this is farther process;my pid = 1
|
|
|
|
|
|
|
|
|
|
此时运行测试脚本:
|
|
|
|
|
## 实验二 信号量 ##
|
|
|
|
|
|
|
|
|
|
$ python3 ./pke-final-1
|
|
|
|
|
|
|
|
|
|
预期得到输出:
|
|
|
|
|
|
|
|
|
|
build pk : OK
|
|
|
|
|
running app5_1 : OK
|
|
|
|
|
test fork : OK
|
|
|
|
|
Score: 20/20
|
|
|
|
|
|
|
|
|
|
### 实验二应用输入: ###
|
|
|
|
|
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
## 应用程序二 ##
|
|
|
|
|
到目前为止,我们已近实现了进程间的简单同步。接下来,我们更进一步,考虑信号量的实现。App5_2的代码如下:
|
|
|
|
|
|
|
|
|
|
#include<stdio.h>
|
|
|
|
@ -501,6 +519,30 @@ Schedule会选取下一个进程,随即进入switch_to,这又是一段汇编
|
|
|
|
|
|
|
|
|
|
上述代码是一个简单的生产者消费者程序,由一个父进程创建两个子进程,父进程作为producer而子进程作为consumer。这里维护了三个信号量,mutex作为互斥信号量,为临界区提供互斥访问,empty用来维护空闲缓冲区,full则用来维护被填充的缓存区。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 实验二任务: ###
|
|
|
|
|
|
|
|
|
|
实验二任务描述:
|
|
|
|
|
|
|
|
|
|
实现`__down`函数,支持app5_2.c的运行
|
|
|
|
|
|
|
|
|
|
实验二预期输出:
|
|
|
|
|
|
|
|
|
|
$ spike obj/pke app/elf/app5_2
|
|
|
|
|
预期得到输出中,父进程生产的数量等于两个子进程消费的数量之和。运行脚本:
|
|
|
|
|
|
|
|
|
|
$ python3 ./pke-final-2
|
|
|
|
|
预期得到输出:
|
|
|
|
|
|
|
|
|
|
build pk : OK
|
|
|
|
|
running app5_2 : OK
|
|
|
|
|
test sema : OK
|
|
|
|
|
Score: 20/20
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 实验二提示: ###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在程序执行的过程中,任务常常会因为某一条件没有达成而进入等待状态,具到的上述的例子,当producer发现没有空闲的缓存区即empty不足时,或者consumer发现full不足时,二者均会进入等待状态。等待条件得到满足,然后继续运行。这种机制,我们可以使用等待队列来实现,等待某一条件的进程在条件未满足时加入到等待队列当中,当条件满足时在遍历对应的队列唤醒进程,并且将进程从队列中删除。
|
|
|
|
|
|
|
|
|
|
在此,我们定义结构体wait_t如下:
|
|
|
|
@ -614,22 +656,11 @@ vaddr是信号量在用户空间的地址,即&empty的值。这里我们将信
|
|
|
|
|
|
|
|
|
|
相应的,在down的代码中需要执行相反的逻辑,首先,需要判断value的值是否大于0,若value值大于0,则可以直接进行减操作。若否则需要将当前进程加入等待队列,并设置其state与wait_state。接着需要调用schedule。当schedule返回时,需要对唤醒标准进行判断,并将进程从等待队列中删除。
|
|
|
|
|
|
|
|
|
|
### 任务三:__down ###
|
|
|
|
|
任务描述:实先`__down`函数,支持app5_2.c的运行
|
|
|
|
|
|
|
|
|
|
$ spike obj/pke app/elf/app5_2
|
|
|
|
|
预期得到输出中,父进程生产的数量等于两个子进程消费的数量之和。运行脚本:
|
|
|
|
|
|
|
|
|
|
$ python3 ./pke-final-2
|
|
|
|
|
预期得到输出:
|
|
|
|
|
|
|
|
|
|
build pk : OK
|
|
|
|
|
running app5_2 : OK
|
|
|
|
|
test sema : OK
|
|
|
|
|
Score: 20/20
|
|
|
|
|
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
### 提交课设 ###
|
|
|
|
|
|
|
|
|
|
至此为止,你已经完成了本实验的所有代码,运行脚本./pke-final:
|
|
|
|
|
|
|
|
|
|
$ python3 ./pke-final
|
|
|
|
|