OS_lab6_实验报告
1. 思考题
想让父进程作为读者,只需要反过来操作就可以了。先关闭写,再进行读操作,最后关闭读并退出即可。代码如下:
1 | default: |
dup 函数的具体内容如下图所示:
可以看到该函数的主要功能是复制文件描述符,即让访问newfdnum的fd或data时,实际访问到的页框是oldfdnum的页框,也就是实现共享页面。
在共享页面之后,oldfdnum所对应的fd的页框的pp_ref会+1,同理pipe的引用也会+1。如果在fd的引用+1之后,pipe的引用还没来得及+1,此时发生了时钟中断并切换了进程,那么此时该fd的引用和pipe的引用数相同,会导致进程在检验另一端是否关闭时返回真,进而导致其误以为读端/写端已经关闭,从而提前结束。
在执行系统调用后,操作系统会陷入内核态,此时会关闭CPU的时钟中断权限,相关代码如下:
可以看到 STATUS_IE 被置零了,说明此时中断被禁止,因此在内核态执行系统调用时不会被时钟中断,是原子操作。
可以解决,若在pageref(pipe) > pageref(fd)
的情况下便没有问题,而如果pageref(pipe) == pageref(fd)
的话,那么当读缓冲区为空,写缓冲区为满时会再次循环直到进程切换两者全部unmap为止。
dup
也会出现同样的问题,先对pipe
进行map,再对fd
进行map即可。目的就是时刻保持pageref(pipe) > pageref(fd)
。
文件打开的过程:
- 用户进程调用open函数,其中传入要打开文件的路径和打开方式。
- 调用fsipc_open函数。
- 调用fsipc与文件系统服务进程通信。
- 通过识别req的种类来调用serve_open函数。
- serve_open先找到要打开的文件的文件控制块,然后申请一个struct Open,并填写相关信息。
- 用户进程申请一个struct Filefd,并通过共享页面来访问文件系统服务进程的struct Filefd。
- 通过其中的文件控制块来在data区域映射文件的磁盘块,调用map函数来与文件系统服务进程共享页面。
如何读取加载ELF文件:先验证elf文件的文件头,看看该文件是否是符合要求的elf文件。之后通过文件头的偏移找到程序头表。遍历每一个程序头表,对每一个程序头表,将其数据按照其规定的虚拟地址进行页面映射。进而完成elf文件的加载。
elf_load_seg()函数的主要功能是将一个程序头表的数据按照程序头表所规定的虚拟地址映射到进程的页表中。其中将一段程序分成了三部分,第一部分是开头未页对齐的部分,第二部分是中段有内容的data段,第三部分是最后的bss段。该函数通过调用load_icode_mapper来每次实现一页的加载。对于bss段,对于申请后的页面,用0来填充,这样就起到了bss段初始值为0的效果。
在 user/init.c 中如下代码片段实现了该功能:
我们用到的shell命令是外部命令,因为我们的user文件夹中有cat.c
ls.c
文件,Linux下的cd指令没有对应的文件,使用时也不需要单独的创建一个子进程。cd 所做的是改变 shell 的 PWD。 因此倘若 cd 是一个外部命令,那么它改变的将会是子 shell 的 PWD,也不会向父 shell 返回任何东西。所以,当前 shell 的 PWD 就不会做任何改变。所有能对当前 shell的环境作出改变的命令都必须是内部命令。 因此如果我们将 cd 做成外部命令,就无法像原来一样改变当前目录了。
两次spawn,四次destroying。
2. 难点分析
1. 管道
管道分为有名管道和无名管道,是半双工的单向管道。其中我们实现的管道只能在父子进程间通信。
pipe函数对两个文件描述符的操作十分重要,两个文件描述符本身是不同的,但他们却指向的是同一个管道,先syscall_mem_alloc
,再syscall_mem_map
,通过fd_omode
限定该文件描述符的读写。
要注意在检测另外一个端是否结束的时候,必须考虑所有中断情况,保证在每一种可能的中断情况下该检查函数都能正确返回。否则可能会出现提前结束的情况。
在_pipeisclosed
函数里,我们发现了env_runs
这个从lab 3就一直跟随着我们的属性的价值,这或许也可以作为其他函数用于判断是否是类原子操作的一个方法。
2. shell
shell主要作用是解析输入的字符串,根据输入指令来完成对应的操作,最后输出相应的结果。
其中需要从磁盘中读取文件并运行,用到了spawn这个函数。这个函数通过传入地址,参数,来创建一个进程运行地址所指向的elf文件。
load_icode_mapper函数已经写过了,这次需要在用户态下实现这个函数,用户态实现二进制文件的加载不像内核态的调用那样简单,但大体的思路差不多,也会用到user_bcopy
、user_bzero
。spawn
函数的填写更是需要看一下ELF文件格式才好理解。而解析shell命令的runcmd
函数相对就简单一点。
判断当前的字符是否是特殊符号可以有一个更快捷的方法,这样可以避免书写过长的switch。
1 |
|
3. 实验体会
个人感觉,lab6难度比lab5小一点,但是需要仔细认真的阅读和理解。需要仔细阅读的代码量也比较小,但需要在理解lab5的基础上才能快速上手。
OS的实验课就到此结束了,学了很多理论知识,也看了很多代码,具体的函数实现也没有想着能够一直记下来,大概能有个印象我觉得就行了。在我看来,这门课学到的其实也不止这些代码(因为感觉以后大概率也用不上),主要是学会了很多有用的工具,把c语言的应用也提升了一个档次,这就足够啦。
4. 原创声明
本实验报告部分参考了北航操作系统lab6实验报告 - 南风北辰 - 博客园 (cnblogs.com)其余部分均为原创。