Nachos系统调用实习报告.docx
- 文档编号:28070580
- 上传时间:2023-07-08
- 格式:DOCX
- 页数:18
- 大小:529.17KB
Nachos系统调用实习报告.docx
《Nachos系统调用实习报告.docx》由会员分享,可在线阅读,更多相关《Nachos系统调用实习报告.docx(18页珍藏版)》请在冰豆网上搜索。
Nachos系统调用实习报告
系统调用实习报告
善良的大姐姐
2015.5.3
一:
总体概述3
二:
任务完成情况3
任务完成列表(Y/N)3
具体Exercise的完成情况3
三:
遇到的困难以及解决方法18
五:
对课程的意见和建议19
六:
参考文献19
一:
总体概述
自lab4我们完成了虚拟内存的实习,可以运行用户程序之后,我们就考虑加入系统调用。
即,用户程序可以通过特定的系统调用,陷入Nachos内核,从而完成特定的目标。
本次lab一共要求完成10个系统调用,包括两大部分,文件系统相关——Create,Open,Close,Read,Write;用户程序相关——Exec,Fork,Yield,Join,Exit。
需要在阅读和理解源码的基础上,知道系统调用的执行流程,进一步修增源代码,实现新增的系统调用功能。
二:
任务完成情况
任务完成列表(Y/N)
Exercise1
Exercise2
Exercise3
Exercise4
Exercise5
Yes
Yes
Yes
Yes
Yes
具体Exercise的完成情况
Exercise1:
源代码阅读
任务:
阅读与系统调用相关的源代码,理解系统调用的实现原理。
完成情况:
1.Syscall.h
概述:
1)定义了每个系统调用对应的系统调用号
2)声明了每个系统调用
2.Exception.cc
概述:
对系统陷入进行处理。
1)从machine的2号寄存器读入系统调用号
2)执行对应的操作代码(需要自己完成)
3)(如果需要)将返回值写回Machine的2号寄存器
3.Start.s
概述:
当用户程序执行一个系统调用的时候,将参数放入2号寄存器,然后跳转到exception.cc执行。
以Halt为例:
4.总结
当用户希望执行一条系统调用的时候:
1)在用户程序中调用
2)当这条语句被OneInstruction函数解析执行时,会判断出这是一条系统调用,转入start.s
3)在start.s中找到系统调用对应的入口(可能需要自己增加),将系统调用号放入machine的2号寄存器,并转入exception.cc
4)在exception.cc中,读出2号寄存器中的系统调用号,执行对应操作
5)必要时,将返回值写回2号寄存器,并注意,将PC前进。
6)指令回到用户程序系统调用的下一条继续执行。
系统调用完成。
为了执行一条系统调用,我们需要完成的部分:
1)
自己写一个用于测试的用户程序的.c文件,并修改test的Makefile,使得用户程序能够被Nachos系统执行。
2)因为本次lab需要写的系统调用,在start.s中都已经写好了,因此我们不需要修改。
同样,syscall.h中,系统调用号和函数声明也都写好了。
(但如果希望自己新增系统调用,这两个文件是需要修改的,修改方式可以参照别的系统调用)
3)补充exception.cc,执行对应的系统调用操作。
Exercise2:
系统调用实现
任务:
类比Halt的实现,完成与文件系统相关的系统调用:
Create,Open,Close,Write,Read。
Syscall.h文件中有这些系统调用基本说明。
Exercise3:
编写用户程序
任务:
编写并运行用户程序,调用练习2中所写系统调用,测试其正误。
EX2+EX3完成情况:
首先,在完成了文件系统的lab之后,为了能够让userprog编译通过,需要修改system.h的makefile,多include几个头文件以及声明extern变量。
否则,会出现:
undefinedreference***这种情况。
其次,我们需要在exception.cc中,加入系统调用入口:
1.voidCreate(char*name)
概述:
1)从4号寄存器读入文件名指针
2)利用指针,从Machine的mainmemory(内存)中,读出文件名
3)用文件名为参数,调用filesystem的create函数,创建文件(默认长度256byte)
4)将PC前移:
(machine.cc中定义)
代码:
(从内存中读出文件名部分)
测试截图:
之后在userprog文件夹下,就出现了a.txt这个文件(因为我是用UNIX的文件系统来做这次lab的)
2.OpenFileIdOpen(char*name)
概述:
1)从4号寄存器读入文件名指针
2)利用指针,从Machine的mainmemory(内存)中,读出文件名
3)用文件名为参数,调用filesystem的open函数,打开文件,返回OpenFile指针(在Nachos系统中,OpenFile相当于是文件描述符)
4)将OpenFile指针写回machine的2号寄存器(machine->WriteRegister(2,(int)openfile))
5)PC前移
代码:
(Openfile写回部分)
测试截图:
(打开失败)(Open成功会配合之后的系统调用再进行测试截图)
3.voidClose(OpenFileIdid)
概述:
1)从4号寄存器中读出OpenFile的指针
2)调用filesystem的close方法,将openfile关闭(自己编写的close方法,其中将传入的OpenFile指针delete掉)
3)PC前移
代码:
(从寄存器中读出Openfile指针+关闭部分)
测试截图:
(Close配合open系统调用测试截图)
4.intRead(char*buffer,intsize,OpenFileIdid)
概述:
1)从4号寄存器读出来装载字符串的指针
2)从5号寄存器读出需要读取的字符串长度
3)从6号寄存器读出OpenFile的指针
4)调用openfile的read函数,从光标位置读出一定长度的字符串,存入临时数组当中
5)将读出来的内容,写回machine的mainmemory中,写入的位置就是字符串指针依次往后。
6)将openfile的read函数的返回值,写入2号寄存器当中。
代码:
测试截图:
(见write部分,二者配合测试。
)
5.voidWrite(char*buffer,intsize,OpenFileIdid)
概述:
1)从4号寄存器读出来装载字符串的指针
2)从5号寄存器读出需要读取的字符串长度
3)从6号寄存器读出OpenFile的指针
4)从machine的mainmemory中,从字符串指针位置开始,连续读出字符串长度个字符
5)将读出来的内容,调用OpenFile的write方法,写入文件当中。
代码:
测试截图:
a)单独测试write
成功写入希望写入的内容
b)配合read一起测试
Exercise4:
系统调用实现
任务:
实现如下系统调用:
Exec,Fork,Yield,Join,Exit。
Syscall.h文件中有这些系统调用基本说明。
Exercise5:
编写用户程序
任务:
编写并运行用户程序,调用练习4中所写系统调用,测试其正确性。
EX4+EX5完成情况:
首先,为了能够使得Join和Exec的系统调用,能够在父子进程之间发生联系,我们需要在thread.h中加入Thread*childThread[MaxThreadSize],用于记录当前线程的子线程的thread指针。
这个数组在thread的构造函数中初始化,初始值均为NULL。
还需要加入Thread*fatherThread变量,用于记录当前线程的父线程(如果存在)。
同样在构造函数中进行初始化,初始赋值为自己。
1.SpaceIdExec(char*name))
概述:
1)从4号寄存器中读出名字指针,并仿照EX2中的做法,从mainmemory中获得名字。
2)新创建一个线程
3)在当前线程的childThread数组中,寻找是否还有空位,如果有,赋值给新创建的线程,标明父子关系,并且将新线程的指针号写回2号寄存器。
接着,将新创建线程的fatherThread指针赋值为currentThread。
新线程这时可以调用Fork,执行一段代码。
最后,PC前移
4)如果没有空位,输出错误信息,将PC前移。
5)Fork执行的代码:
参照progtest中,startprocess函数,先是打开这个用户程序文件,然后新创建一个地址空间,接着初始化machine,最后让machine->run。
代码:
a)Fork之后调用的函数:
b)处理系统调用的代码:
测试截图:
(和之后的join+exit一起测)
2.intJoin(SpaceIdid)
概述:
1)从4号寄存器中读出要等待的子线程的OpenFile指针
2)找到自己的childThread数组中,这个OpenFile指针对应的位置
3)While循环,当这个位置不为NULL时,调用currentThread->Yield(),等待子线程结束
4)跳出while循环后,将子线程的结束状态写入2号寄存器,PC前移
代码:
测试截图:
(配合Exec和Exit一起测)
3.voidExit(intstatus)
概述:
1)由于lab4虚拟内存中,需要在exit系统调用中释放page和清空tlb,因此此处继续保留这两个功能。
2)判断:
如果当前exit的线程不是主线程:
如果当前线程有父线程,将父线程中对应的自己设置为NULL(表明自己已经要退出啦!
),并且调用currentThread->Finish()
3)如果是主线程:
PC前移
测试截图:
(配合之前的Exec和Exit)
a)测试函数:
Mytest函数:
b)测试截图:
4.voidFork(void(*func)())
概述:
1)从4号寄存器中读出要执行的用户函数的起始PC
2)新创建一个线程,并参照Exec中的步骤,完成父子关系的建立
3)PC前移。
4)在Fork的系统函数中,完成准备工作:
将父进程的用户空间(Pagetable)拷贝到子进程中;将machine当前的PC值修改为1)中的PC值,NextPC值修改为1)中的PC值+4;将修改完成的这套寄存器值存放到新线程的寄存器当中(其中,除了两个PC值,其余的都和父进程相同!
)
5)Machine->Run(),就可以跳转到用户函数执行了。
注意:
1)开始时刻,子进程和父进程的寄存器值,除了PC不同,别的都相同。
2)子进程和父进程使用同样的pagetable。
3)子进程和父进程共享machine的栈空间(因此子进程不必重设栈空间)
代码:
测试截图:
5.voidYield()
概述:
1)PC前移
2)调用currentThread->Yield()
(注意,此处二者不能调换顺序,因为一旦当前线程yield了,下一次再上CPU,是不会从没执行完的系统调用处开始,而是会从PC当前对应的指令开始的。
如果没有提前将PC前移,那么下一次,还会执行Yield系统调用,就陷入了死循环)
代码:
测试截图:
a)
Mytest函数
测试函数
b)测试结果
三:
遇到的困难以及解决方法
困难1:
Userprog编译不通过
编译userprog一直提示undefinedreference:
thread/synchdisk啦,bitmap啦……感觉很疑惑。
原来是在system.h中,IFDEFUSER_PROGRAM中,需要include这几项。
详见EX2+EX3完成情况中对此的说明及截图。
困难2:
当对同个文件执行read和write时,如果先write再read,会出现读出来的东西都是乱码的情况
这是由于二者共享了同个文件的文件指针(OpenFile指针),如果先write,此时文件指针已经移到了文件的末尾,再执行read,就是从未知的位置读出东西,自然是乱码。
困难3:
在写Exec部分的时候,当从子线程切换到主线程的时候,主线程反复执行Exec系统调用
一开始以为是寄存器没更新之类的问题。
原来是忘记在Exec的系统调用末尾让PC+4,由于再次切换回主线程的时候,主线程会从当前PC位置开始执行,如果PC没有+4,就会一直反复执行Exec这条系统调用。
四:
收获及感想
这是最后一个必做Lab,一开始觉得不就是系统调用嘛,应该很简单!
但事实上,两句话就能说明白的一个系统调用,真正写起来,还是要面对许多细节问题。
譬如陷入系统调用之后,参数的传递,这里就涉及是否要从machine的mainmemory中读出东西,还是寄存器里的值直接可以使用,都是需要仔细考虑的。
包括之后的Fork和Exec系统调用,要考虑如何共享地址空间之类的问题,感觉这次lab细节很重要。
五:
对课程的意见和建议
不知道为什么,测试譬如Open或者Create系统调用的时候,文件名经常会出现一串乱码(如EX4+EX5完成情况中,yield系统调用中的截图),但是我检查过,指针什么的都是正确的,我怀疑是ReadMem或者WriteMem写的哪里有问题,这是原本Nachos代码的问题。
六:
参考文献
博客:
博客:
PS:
这两篇博客里头都只有内容概述,基本没有代码。
代码都是我自己写的,测试截图也是辛辛苦苦调试测试之后截的……希望助教哥哥不要误会我有抄袭之嫌~
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Nachos 系统 调用 实习 报告