您好,欢迎访问三七文档
系统调用实习报告姓名李炜学号1100012810日期5月6日2目录内容一:总体概述...................................................................................................................3内容二:任务完成情况...........................................................................................................3任务完成列表(Y/N).....................................................................................................3具体Exercise的完成情况................................................................................................3内容三:遇到的困难以及解决方法.......................................................................................5内容四:收获及感想...............................................................................................................6内容五:对课程的意见和建议...............................................................................................6内容六:参考文献...................................................................................................................63内容一:总体概述这次实习主要是实现有关文件系统的系统调用,以及更加高级的有关线程的系统调用。【用简洁的语言描述本次lab的主要内容;阐述本次lab中涉及到的重要的概念,技术,原理等,以及其他你认为的最重要的知识点。这一部分主要是看大家对lab的总体的理解。要求:简洁,不需要面面俱到,把重要的知识点阐述清楚即可。】内容二:任务完成情况任务完成列表(Y/N)Exercise1Exercise2第一部分Y第二部分YY第三部分YY具体Exercise的完成情况Exercise1:Syscall.hStart.sException.cc这两个文件需要一起结合着看,syscall这个头文件可以说没有没有相对应的.cc文件,它对应的实际上是start.s文件,就我们关心的几个函数来说,create,open,close,read,write这几个函数都是在syscall里面定义了函数的signature,而在start.s中把这些函数对应的系统调用号SC_...放入二号寄存器中,使得exception里面的exceptionhandler可以识别出对应的系统调用类型,来进行相应的处理。由于系统调用不能通过传统的方式传递参数,在start里面的注释中可以看出,4~7号寄存器可以用来传递参数,而2号寄存器在exception里面应该作为传递返回值的寄存器。由于exception在用户线程部分就已经用过多次,这里就不介绍了。Exercise2、3:系统调用和调试就像之前实现tlbmiss和pagefault的时候一样,这次也是主要修改exception中的exceptionhandler,其实有了前面的基础,这个练习相对简单,但是最麻烦的是传递参数的问题,虽然知道是通过4个寄存器来传递参数,但是实际编写的时候发现并不是这个简单,比如create需要的参数是一个char*指针,指向的是一个字符串,这个字符串是要创建的文件的名字,开始的时候我直接把从4号寄存器中读出的int值强制转化为char*直接输出,发现出现了段错误,其它任何提示都没有,直接就头大了,后来经过同学提醒,想起了这里的内存空间应该是一致的内存空间而不是宿主机的内存空间,如果直接使用char*转化过的那个int型地址,得到的其实是宿主机上的相应地址而不是nachos的内存地址。这样就是说我们应该去machine-mainMemory里面找这个字符串,有用的还是得到的那个地址,但是麻烦的是readMemory这个函数只接受int*作为写入的地址,也就是说如果想直接向我自己4定义的char数组中一个一个地写入char,是不可以的,因为这样会覆盖掉相邻位置的字节。于是只能模拟读int的方式,一次读入4个字节,每次把地址加4(而不是1)。这样操作之后终于可以正确地输出这个名字参数了。接着把名字和文件大小(我默认为一个sector的大小)作为参数传递给filesys的create,就可以创建文件了。但是开始写的时候改正完这个错误还不算完,程序运行的时候就像死循环一样,不停地在create里面转,后来想到之前写的tlbmiss的部分,明白了这里也应该改pc指针的值,把nextpc的值赋给pc寄存器,这样之后create终于可以很好地运行了。完成了一个系统调用,其它的其实就简单多了,大多数是函数的包装。对于open来说,还需要在当前线程的地址空间中增加把这个打开文件号的功能,我在地址空间中定义了一个存放打开文件的数组,每次找空的位置放入新的打开文件。这样open就基本完成了,close就是delete数组中这个文件号对应的文件,然后把数组中这个位置置为null即可。对于write和read来说,需要有读文件写入buffer的操作或者从buffer里面读然后写入到文件,因为openfile的read和write都是根据seekposition的位置读写的,具有不确定性,于是我选择了使用readat和writeat,和读文件名的时候不同的是,这里buffer和mainMemory都是char类型的,所以在写buffer的时候可以一个字节一个字节地写。而在写文件的时候还需要判断当前文件的大小是不是足够盛放这些要写的内容,如果不够的话,那么还需要先allocatemore。测试程序的话,很简单,就是仿照halt.c的写法,先create,然后open,write,read,close最后halt就可以了。这里我碰到的一个问题是不能在main里面定义变量(所谓的局部变量),必须在main外面定义全局变量,否则编译不会通过。Exercise4、5:为了实现Exec系统调用,可以仿照progtest里面的StartProcess,于是我基本上按照StartProcess的实现方法在Exception.cc里面加入一个同名函数StartProcess,不同的只是在于这里我需要从寄存器和machine的mainMemory里面读取出可执行文件名,因为需要支持多线程执行不同的程序,所以我在ExceptionHandler里面的SC_Exec里面还需要使用新创建的thread,并且用这个新的thread去Fork出StartProcess这个函数,这样,即使多次使用系统调用Exec,也可以并行地执行了。对于Join来说,主要是需要被Join的线程等待join的线程,可以通过在调度到父线程时不断地yield来实现。为了明确什么要等待的子线程是不是已经结束运行了,需要在父线程中维护一个数组来记录该线程都有哪些子线程,这样也就给了join目标参数,我在thread类中定义了这样一个数组,并且定义了相应的将子线程加入到数组中的函数AddChild,这个函数遍历数组,找到空位后将子线程指针加入到空位中,返回刚才加入的数组下标。并且需要在thread类中加入join函数,这个函数接收子线程号作为参数,如果数组中这个下标的线程还不是NULL(通过while循环来判断),那么就一直Yield。这样的机制必须允许子线程到父线程中修改子线程数组,于是我在AddChild函数里面,把currentThread赋给子线程的parentThread指针,并且在thread的finish函数里面把父线程的具有子线程spaceId(在exec系统调用中赋给子线程)的数组元素赋成NULL,这样当父线程再次检查的时候就可以跳出while循环了。对于Exit,比较简单直接调用currentThread的finish函数并且使得pc指针前进即可。从syscall.h中可以看出,在Nachos中Fork和Exec的区别在于Exec会创建不同的Addrspace而Fork和父线程使用相同的Addrspace,Nachos和Linux的Fork的不同点在于Linux只需要5复制父线程的空间即可,而Nachos直接要求Fork出来的线程运行一个函数。于是在ExceptionHandler中我加入对SC_Fork的支持,首先我new出一个thread,并把它加入到currentThread的子线程中,接下来问题来了,怎么使得子线程和父线程的space是一样的呢?我在Addrspace里面加入了一个新的构造函数,这个构造函数接受另外一个space作为参数,并且把打开文件列表和页表都复制到自己这里。这样之后子线程的空间基本和父线程一样了,然后我又把currentThread的userRegisters寄存器的内容复制到新创建的thread里面(就像restorestate一样),那么怎么才能得到参数(传进来的函数指针),并且运行这个函数呢?这个问题困扰了我很久,最后我试了很多中可能的结果(4号寄存器中,4号寄存器对应的主存中),最后得出结论,这个地址在4号寄存器中,而我需要做的就是把这个寄存器中的值赋给pc指针,并且设置相应的nextPC指针。因为thread要运行涉及到状态转换,所以我写了一个包装函数,里面首先restoreUserState,然后restorespace里的状态,之后读出4号寄存器,设置成PC指针的值,4号寄存器值加4设置成NextPC的值,最后machine-Run,这样就完成了包装函数了,在handler里面只要使创建的thread去fork这个函数就好了,最后使得pc前进。对于yield调用,我直接调用了currentThread的yield并且使pc指针前进。【对于阅读代码类的exercise,请对其中你认为重要的部分(比如某文件,或某个类、或某个变量、或某个函数……)做出说明。对于要编程实现的exercise,请对你增加或修改的内容作出说明。如果增加或修改了某个类,请写出这个类写在那个文件中,它的的功能是什么,你自己添加或修改的成员变量以及成员函数有哪些,它们的功能是什么;特别的,对于复杂的函数,请说明你的实现方法。不需要贴具体的实现代码。要求:表述清楚即可,可采用图表来辅助说明,不需要贴代码】内容三:遇到的困难以及解决方法困难1在系统调用中,如何得到字符串参数的真正地址,这个涉及到了宿主机内存和虚拟机内存的关系。困难2……【描述你在实习过程中遇到的困难,是与实习直接相关的技术方面的难题。突出说明你是如何攻克这些难关的。要求:只需写一下有较深体会的困难。如果觉得整个过程都比较简单的话此部分可不用写。】6内容四:收获及感想……【自己的收获,任何关于实习的感想,可以是技术方面的或非技术方面的,可随意发挥。要求:内容不限,体裁不限,字数不限,多多益善,有感而发。】内容五:对课程的意见和建议……【请写下你认为课程需要改进的地方,任何方面,比如进度安排、难易程度、课堂讲解、考核方式、题目设置……甚至如果你认为源代码哪里写得不好也欢迎提出。各位同学反馈的
本文标题:系统调用实习报告
链接地址:https://www.777doc.com/doc-5327622 .html