OS_Lab6
思考题
1.
示例代码中,父进程操作管道的写端,子进程操作管道的读端。如果现在想让父进程作为“读者”,代码应当如何修改?
- 在执行
fork()
后,case 0
和default
的部分相互调换位置就可以。这样父进程会关闭写并读取内容,子进程则会关闭读并发送内容。
2.
上面这种不同步修改 pp_ref 而导致的进程竞争问题在 user/fd.c 中的dup 函数中也存在。请结合代码模仿上述情景,分析一下我们的 dup 函数中为什么会出现预想之外的情况?
- dup函数将一个文件描述符对应的内容映射到另一个文件描述符,并把文件内容映射过去。
- 原来的执行顺序,会先把文件描述页面映射,再映射文件内容,这样如果在文件内容没有映射完毕时进程被中断并替换,另一程序会因为文件描述页已经存在认为文件映射完成,从而造成错误。
3.
阅读上述材料并思考:为什么系统调用一定是原子操作呢?如果你觉得不是所有的系统调用都是原子操作,请给出反例。希望能结合相关代码进行分析。
- 在我们实验的操作系统中,进入系统调用时会屏蔽中断响应,因此其是原子操作。
.macro CLI
mfc0 t0, CP0_STATUS
li t1, (STATUS_CU0 | 0x1)
or t0, t1
xor t0, 0x1
mtc0 t0, CP0_STATUS
.endm
4.
仔细阅读上面这段话,并思考下列问题
• 按照上述说法控制 pipeclose 中 fd 和 pipe unmap 的顺序,是否可以解决上述场景的进程竞争问题?给出你的分析过程。
• 我们只分析了 close 时的情形,那么对于 dup 中出现的情况又该如何解决?请模仿上述材料写写你的理解。
- 可以。未完成情况下pipe的引用大于fd的引用,先对fd进行unmap不会达成pipe引用次数与fd引用次数相等而导致退出,因而不会发生误判。
- 先映射全部的文件内容,再去映射文件控制块。
5.
bss 在 ELF 中并不占空间,但 ELF 加载进内存后,bss 段的数据占据了空间,并且初始值都是 0。请回答你设计的函数是如何实现上面这点的?
- ELF文件中记录了程序在文件中的大小与在内存中的大小,两者之差包含bss段大小。在装载入内存时把空间预留并清零即可。
6.
为什么我们的 *.b 的 text 段偏移值都是一样的,为固定值?
- 文件中是分段管理的,各段起始地址固定。
7.
在哪步,0 和 1 被"安排"为标准输入和标准输出?请分析代码执行流程,给出答案。
// user/init.c
close(0);
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);
// user/console.c
int
opencons(void)
{
int r;
struct Fd *fd;
if ((r = fd_alloc(&fd)) < 0)
return r;
if ((r = syscall_mem_alloc(0, (u_int)fd, PTE_V|PTE_R|PTE_LIBRARY)) < 0)
return r;
fd->fd_dev_id = devcons.dev_id;
fd->fd_omode = O_RDWR;
return fd2num(fd);
}
实验难点
难点主要存在于把ELF文件装载入内存的操作。
- 首先要明确:装载进程(父进程)和子进程对内存的映射(虚拟地址到物理地址)并不相同。
因此,当我们要向子进程的一页进行操作时,需要如下进行:- 为子进程分配新页面
- 为父进程申请空白页面,并映射到子进程新申请的页面
- 把文件数据装载入父进程新申请的页面
- 其次是解读ELF文件格式,此项工作在前面lab中已经做过,但有所遗忘。重新学习后算是较快完成。
体会与感想
用时
用时基本与Lab5持平,低于Lab3、4.
体会
迭代太可怕了。这次最终作业,成功地检验了自己一学期的成果。正如指导书的灵魂拷问:“你的大厦倒了吗?”(其实没有这么问)。
多少与这次测试点的设置有关,我测试点part1一直没有全部通过,仔细检查part1的代码并认定没有问题后,怀着悲伤——不,绝望,我开始了“重建大厦”的旅途。
但最后发现是part2的错误关联了part1……
当然,这也让我对迭代有了新的认知。