lab6实验报告
一、实验思考题
Thinking6.1
1 #include <stdlib.h>
2 #include <unistd.h>
3
4 int fildes[2];
5 /* buf size is 100 */
6 char buf[100];
7 int status;
8
9 int main(){
10
11 status = pipe(fildes);
12
13 if (status == -1 ) {
14 /* an error occurred */
15 printf("error\n");
16 }
17
18
19 switch (fork()) {
20 case -1: /* Handle error */
21 break;
22
23
24 case 0: /* Child - reads from pipe */
25 close(fildes[1]); /* Write end is unused */
26 read(fildes[0], buf, 100); /* Get data from pipe */
27 printf("child-process read:%s",buf); /* Print the data */
28 close(fildes[0]); /* Finished with pipe */
29 exit(EXIT_SUCCESS);
30
31
32 default: /* Parent - writes to pipe */
33 close(fildes[0]); /* Read end is unused */
34 write(fildes[1], "Hello world\n", 12); /* Write data on pipe */
35 close(fildes[1]); /* Child will see EOF */
36 exit(EXIT_SUCCESS);
37 }
38 }
示例代码中case 0
段与default
段分别是子进程和父进程,子进程读,父进程写。如果想让父进程作为“读者”,可以交换case 0
和default
的语句块。
Thinking6.2
一种可能的情况是,父子进程如果出现
- 父进程已经关闭管道
p[0]
,准备执行ispipeclosed(p[1])
函数。 - 子进程执行
dup(p[1])
,在刚刚完成dup
的fd
,但还没有进入dup
的Pipe
结构体部分。
p[1]
的reference
次数为3,p[0]
的reference
为1,Pipe
结构体所在页面映射在父进程p[0]
和子进程的p[0]
和p[1]
的fdData
部分,reference
次数同样为3。
这时在父进程的ispipeclosed(p[1])
进行判断pageref(wfd) == pageref(pipe)
将会判断为真。
Thinking6.3
用户在调用syscall
后,操作系统不应该允许其他程序执行,直到调用完毕返回。
系统调用进入时,操作系统会执行关中断指令。这在syscall.S
的CLI
中体现。所以此时系统调用并不能被打断。相关的sys_ipc_recv
可以理解为设置进程的相关状态,进入recv
状态,这一个操作也是原子操作,不能被打断。
Thinking6.4
可以解决竞争。当ref_a > ref_b
,如果先减少ref_a
再减少ref_b
可能会造成中间有短暂的ref_a == ref_b
的环节,但是改变顺序之后先ref_b
减少所以不会出现中间有相等的过程,可以解决该问题。
dup
是个增加的过程,所以为了避免出现相等的中间过程时被竞争,应该先让大的一个增加,再增加小的。
// ref_a > ref_b
ref_a++;
ref_b++;
Thinking6.5
.bss
段在加载过程中,会根据其中的memsz
属性分配内存空间,并进行清零的操作。
Thinking6.6
在链接部分user.lds
中规定了.text
段首先被链接,而后面的有可能受到前面段大小的影响,但.text
段的偏移是固定的。
. = 0x00400000;
_text = .;
.text : {
*(.text)
*(.fixup)
*(.gnu.warning)
}
Thinking6.7
在初始化中,init.c
,dup(0, 1)
。
if ((r = opencons()) < 0)
user_panic("opencons: %e", r);
if (r != 0)
user_panic("first opencons used fd %d", r);
if ((r = dup(0, 1)) < 0)
user_panic("dup: %d", r);
二、实验难点图示
首先是对于整个流程的理解,以及关于前面lab
的隐藏bug
。由于之前页面映射机制的bug
,导致后续实现时总是在15到20分之间波动。没写spawn
是20,写了却变成15,一度难以定位bug
。
spawn
- 文件系统
- 进程
- 页面映射
- 初始化
- 执行
文件系统部分通过调用open
相关内容打开对应的文件。然后利用syscall_env_alloc
创建子进程。接着将目标程序加载到子进程的地址空间中,分配相关的物理页面。之后进行栈、堆等空间的初始化工作,并设置相关的参数。最后标记为子进程可执行。
pipe
竞争问题
需要有并发的思想。
三、体会与感想
本次实验花费大量时间在bug
的定位上,因为一个小疏忽在这里浪费了很多时间,不过还是很高兴最后能够找到。spawn
函数的设计和管道pipe
之间并发存在的竞争相对来说是个难点。os
的课下lab
已经全部结束,回顾整个lab
确实能让自己的能力提高不少,不同于oo
的巨大工程需要手写几千行代码,os
关于工程的阅读能力有很大的锻炼,需要理解整个操作系统如何运作。最后还要感谢一路以来老师、助教、同学的陪伴!