实验二:多道程序设计
2.1实现文件系统调用
1.题目要求
实现文件系统调用(create、open、read、write、close和unlink,记录在syscall.h中)。您将在UserProcess.java中看到halt的代码;最好也在这里放置新的系统调用。请注意,您没有实现文件系统;相反,您只是让用户进程能够访问我们为您实现的文件系统。
2.问题分析与解决方案
基于对问题要求文档的阅读理解,本次任务是要实现一些列系统调用方法。其中一些系统调用需要与文件操作相关联,所以要理解我们这个nachos系统的文件管理机制。按照要求文档的说明,文件系统是已经实现了的,我们只需要理解这个文件系统并且学会利用它。
在实现几种系统调用的时候,涉及到多个对文件的处理,需要考虑以下细节问题:
- 一个文件不能多次创建
- 已经打开的文件是不能直接删除的
- 一个文件可以被打开多次 打开不存在的文件时先创建再打开
为了解决以上问题,我们创建了两个哈希表来管理文件的状态和打开次数,分别是processfiles和files。在执行相关的操作时需要根据这两个表进行判断,条件允许再进行对文件的下一步操作。
①create系统调用:
1.根据形参fileAddress, 从虚拟内存读取文件名,长度不能超过256。
2.在file表判断文件名是否存在,不存在的话存入,状态记为1
3.如果文件不存在,就直接创建该文件。首先寻找是否还有可以存放的位置,如果超出了数量限制就会退出,创建失败。如果仍有空间可以创建,调用 fileSystem.open(filename, true)。同时在processfiles表中记录。
4.将fileAddress对应的openfile数组的位置放入刚刚创建的文件描述符
②open系统调用:
1.根据形参fileAddress, 从虚拟内存读取文件名,长度不能超过256。
2.在file表判断文件名是否存在,不存在的话存入,状态记为1,如果存在则状态加1,表示打开一次。
3.如果文件不存在,就直接创建该文件。首先寻找是否还有可以存放的位置,如果超出了数量限制就会退出,创建失败。如果仍有空间可以创建,调用 fileSystem.open(filename, false)打开文件即可。同时在processfiles表中记录。
4.将fileAddress对应的openfile数组的位置放入刚刚创建的文件描述符
③read系统调用:
该系统调用涉及三个输入参数: 文件描述符, 写入的内存地址, 读取的字节数。
1.首先判断打开条件,如果打开文件大于16个或者小于0个,或者打开文件列表为空,即出错,返回。
2.创建临时数组temp,将文件的内容保存在temp中,并返回写入的字节数。
3.如果返回的字节数小于0即为出错,返回。
4.将读到的内容写入虚拟内存相应地址。
5.返回写入内存的字节数。
④write系统调用:
该系统调用涉及三个输入参数: 文件描述符, 读取内存的地址, 写入的字节数。
1.首先判断打开条件,如果打开文件大于16个或者小于0个,或者打开文件列表为空,即出错,返回。
2.创建临时数组 temp,将虚拟内存中的内容保存在temp中,并返回读出的字节数
3.如果返回的字节数小于0即为出错,返回。
4.将读到的内容写入磁盘。
5.如果写入的数据长度小于读出的数据长度,则错误,返回。
⑤close系统调用:
1.首先判断打开条件,如果打开文件大于16个或者小于0个,或者打开文件列表为空,即出错,返回。
2.如果文件已经打开一次,修改processfiles和files状态,同时关闭文件
3.把 openfile 数组中 fileDescriptor 中的位置置为null
⑥unlink 系统调用:
1.根据形参fileAddress, 从虚拟内存读取文件名,长度不能超过256。
2.如果文件不存在则不用删除
3.如果文件处于打开状态,则不能删除
4.否则删除文件。
2.2完成对多道程序的支持
1.题目要求
实现对多道程序设计的支持。我们提供给您的代码仅限于一次运行一个用户进程;您的工作是使它对多个用户进程有效。
2.问题分析与解决方案
①loadSections
该方法为每个进程分配内存,将coff块导入进内存,如果成功,进程就会执行(进程初始化会失败的最后一步)
1.首先判断需要的页的数量是否多于剩余物理页数量,如果物理页数量少于需要的页的数量,则内存分配失败,并提示缺少物理地址
2.得到需要的物理页号后,实例化TranslationEntry类型的页表pageTable数组,从空闲物理页表号链表(memoryLinkedList)中取出一个空闲物理页号。在页表数组中实例化一个TranslationEntry对象,并为其赋上相应的虚拟页号和物理页号等信息。
3.对于coffSection的每个section对象,通过for循环得到每一页页号,修改其权限为readonly,然后根据物理地址装入页。
②readVirtualMemory
该方法涉及四个参数:要读的虚拟内存的首字节、数据存放的数组、写入数组的首字节、从虚拟内存转换到数组的字节数。从进程的虚拟内存中将数据复制到指定的数组,这个方法处理地址转换的细节。这个方法在发生错误时不能损坏当前进程,但是应该返回成功复制的字节的数量,如果没有数据复制返回0。
1.计算页号并判断页号的合法性。
2.计算页偏移量。
3.根据页偏移量计算该页剩余容量。
4.比较页剩余量和剩余传输量,取其中的较小值作为将要复制的数据长度。
5.计算相应物理内存地址,虚拟页号对应的物理页号*页大小+页偏移量
③writeVirtualMemory
与read类似,要先利用页表将逻辑地址转换为物理地址然后再将数组数据复制到内存中,并注意取页面时要检查页面是否为只读。
2.3实现系统调用
1.题目要求
实现系统调用(exec, join, and exit, also documented in syscall.h)
2.问题分析与解决方案
①exce系统调用
该系统调用涉及三个参数:文件名地址,参数个数,参数地址。
1.首先从虚拟内存中读取文件名,且文件名长度不能超过256,之后判断文件名的合法性:文件名不能为空,参数个数不能小于0,地址不能小于0,地址不能超过numPages*pageSize
2.将文件内容读入虚拟内存
3.创建子进程
4.设置当前线程为该子进程的父进程,再将子进程加入子进程列表,并返回子进程的进程号
②join系统调用
该系统调用涉及两个参数: join线程的进程号,子进程的返回值
1.先通过pid确定进程,再检查其子进程表,遍历子进程链表,确定join的进程是子进程。如果子进程编号不在其中,则出错返回
2.获得join的锁,使该进程睡眠直到被子进程唤醒
3.子进程唤醒后释放锁,将子进程的运行状态存入父进程的内存中。
4.判断进程是否正常结束,然后返回
③exit系统调用
1.首先关闭coff文件。
2.关闭该子进程openfile中所有打开的文件,并且把位置内容置为null。
3.如果该进程有父进程,就将其唤醒。并从父进程的子进程表中删除这个子进程。
3.判断当前正在运行的进程数量是否为1,如果只剩最后一个进程,执行停机指令
2.4实现彩票调度
1.题目要求
实现彩票调度程序(将其放在threads/lottery scheduler.java中)。注意,这个类扩展了PriorityScheduler,您应该能够重用该类的大部分功能;彩票调度器不应该是大量的额外代码。唯一的主要区别是用于从队列中挑选线程的机制:举行抽签,而不是仅仅挑选优先级最高的线程。您的彩票计划程序应该实现优先捐赠。(注意,由于这是一个彩票调度程序,优先级反转实际上不会导致饥饿!但是,无论如何,您的调度程序必须进行优先级捐赠。)
2.问题分析与解决方案
彩票调度中持有彩票的获取方式是进程自己的彩票数加上自己等待队列中所有进程的彩票数,系统会在0至彩票总数之间随机生成一个数字表示抽取的彩票。彩票越多,下次得到的运行机会就越大。
3.变量说明:
UserProcess类:用户进程载入等方法
LotteryScheduler类:实现彩票调度程序
源代码:
2.1相关代码
private int handleCreate(int fileAddress) {
String filename = readVirtualMemoryString(fileAddress, 256);
//从虚拟内存读取文件名,长度不能超过256
if (filename == null)
return -1;
int fileDescriptor=-1;
if(files.contains(filename)) {
int i=files.get(filename)+1;
files.put(filename, i);
}
else {
files.put(filename, 1);
}
if(processfiles.containsKey(filename))
return processfiles.get(filename);
fileDescriptor = findEmpty();
if (fileDescriptor==-1)
return -1;
else{
openfile[fileDescriptor]=ThreadedKernel.fileSystem.open(filename, true);
//true代表如果不存在就创建
processfiles.put(filename, fileDescriptor);
}
System.out.println("create success");
return fileDescriptor;
}
private int handleOpen(int fileAddress){
String filename=readVirtualMemoryString(fileAddress,256);
if (filename == null)
return -1;
int fileDescriptor=-1;
if(files.contains(filename)) {
int i=files.get(filename)+1;
files.put(filename, i);
}
else {
files.put(filename, 1);
}
if(processfiles.containsKey(filename))
return processfiles.get(filename);
fileDescriptor = findEmpty();
if (fileDescriptor == -1)
return -1;
else {
openfile[fileDescriptor] = ThreadedKernel.fileSystem.open(filename,false);
System.out.println("open success");
processfiles.put(filename, fileDescriptor);
return fileDescriptor;
}
}
private int handleRead(int fileDescriptor,int bufferAddress,int length){
if(fileDescriptor>15||fileDescriptor<0||openfile[fileDescriptor]==null)
return -1;
byte temp[]=new byte[length];
int readNumber=openfile[fileDescriptor].read(temp, 0, length);
if(readNumber<=0)
return 0;
int writeNumber=writeVirtualMemory(bufferAddress,temp);
//把读到的内容写到虚拟内存
return writeNumber;
//返回写入的字节数
}
private int handleWrite(int fileDescriptor,int bufferAddress,int length){
if(fileDescriptor>15||fileDescriptor<0||openfile[fileDescriptor]==null)
return -1;
byte temp[]=new byte[length];
int readNumber=readVirtualMemory(bufferAddress,temp);
//从虚拟内存里面读出来,然后写到内存
if(readNumber<=0)
return 0;
int writeNumber=openfile[fileDescriptor].write(temp, 0, length);
if(writeNumber<length)
return -1;
return writeNumber;
}
private int handleClose(int fileDescriptor){
if(fileDescriptor>15||fileDescriptor<0||openfile[fileDescriptor]==null)
return -1;
else {
String filename=hashbl(processfiles,fileDescriptor);
if(processfiles.containsKey(filename))
processfiles.remove(filename);
if(files.containsKey(filename)) {
int i=files.get(filename)-1;
files.put(filename, i);}
openfile[fileDescriptor].close();
openfile[fileDescriptor]=null;
System.out.println("close success");
return 0;
}
}
private int handleUnlink(int fileAddress){
String filename=readVirtualMemoryString(fileAddress,256);
if(filename==null)
return 0;
if(files.get(filename)>0)
return -1;
else {
if(ThreadedKernel.fileSystem.remove(filename))
return 0;
else
return -1;
}
}
2.2相关代码
protected boolean loadSections() {
UserKernel.allocateMemoryLock.acquire();
if (numPages > Machine.processor().getNumPhysPages()) {
coff.close();
Lib.debug(dbgProcess, "\t insufficient(缺少物理地址) physical memory");
UserKernel.allocateMemoryLock.release();
return false;
}
pageTable=new TranslationEntry[numPages];
for(int i=0;i<numPages;i++){
int nextPage=UserKernel.memoryLinkedList.remove();
pageTable[i]=new TranslationEntry(i,nextPage,true,false,false,false);
}
UserKernel.allocateMemoryLock.release();
for (int s = 0; s < coff.getNumSections(); s++) {
CoffSection section = coff.getSection(s);
Lib.debug(dbgProcess,
"\tinitializing " + section.getName() + " section (" + section.getLength() + " pages)");
for (int i = 0; i < section.getLength(); i++) {
int vpn = section.getFirstVPN() + i;
pageTable[vpn].readOnly=section.isReadOnly();
section.loadPage(i,pageTable[vpn].ppn);
}
}
return true;
}
public int readVirtualMemory(int vaddr, byte[] data, int offset, int length) {
Lib.assertTrue(offset >= 0 && length >= 0 && offset + length <= data.length);
byte[] memory = Machine.processor().getMemory();
if(length>(pageSize*numPages-vaddr))
length=pageSize*numPages-vaddr;
if(data.length-offset<length)
length=data.length-offset;
int transferredbyte=0;
do{
int pageNum=Processor.pageFromAddress(vaddr+transferredbyte);
if(pageNum<0||pageNum>=pageTable.length)
return 0;
int pageOffset=Processor.offsetFromAddress(vaddr+transferredbyte);
int leftByte=pageSize-pageOffset;
int amount=Math.min(leftByte, length-transferredbyte);
int realAddress=pageTable[pageNum].ppn*pageSize+pageOffset;
System.arraycopy(memory, realAddress, data,offset+transferredbyte, amount);
transferredbyte=transferredbyte+amount;
}while(transferredbyte<length);
return transferredbyte;
}
public int writeVirtualMemory(int vaddr, byte[] data, int offset, int length) {
Lib.assertTrue(offset >= 0 && length >= 0 && offset + length <= data.length);
byte[] memory = Machine.processor().getMemory();
if(length>(pageSize*numPages-vaddr))
length=pageSize*numPages-vaddr;
if(data.length-offset<length)
length=data.length-offset;
int transferredbyte=0;
do{
int pageNum=Processor.pageFromAddress(vaddr+transferredbyte);
if(pageNum<0||pageNum>=pageTable.length)
return 0;
int pageOffset=Processor.offsetFromAddress(vaddr+transferredbyte);
int leftByte=pageSize-pageOffset;
int amount=Math.min(leftByte, length-transferredbyte);
int realAddress=pageTable[pageNum].ppn*pageSize+pageOffset;
System.arraycopy(data, offset+transferredbyte, memory, realAddress, amount);
transferredbyte=transferredbyte+amount;
}while(transferredbyte<length);
return transferredbyte;
}
2.3.相关代码
private int handleExec(int fileAddress,int argc,int argvAddress){
String filename=readVirtualMemoryString(fileAddress, 256);
if(filename==null||argc<0||argvAddress<0||argvAddress>numPages*pageSize)
return -1;
String[] args=new String[argc];
for(int i=0;i<argc;i++)
{
byte[] argsAddress=new byte[4];
if(readVirtualMemory(argvAddress+i*4,argsAddress)>0)
args[i]=readVirtualMemoryString(Lib.bytesToInt(argsAddress, 0), 256);//?
}
UserProcess process=UserProcess.newUserProcess();
process.parentProcess=this;
childProcess.add(process);
if(!process.execute(filename, args))
return -1;
for(int i=0;i<process.parentProcess.childProcess.size();i++) {
System.out.print(process.parentProcess.childProcess.get(i).pid+" ");
}
System.out.println();
return process.pid;
}
private int handleJoin(int pid,int statusAddress)
{
UserProcess process=null;
for(int i=0;i<childProcess.size();i++){
if(pid==childProcess.get(i).pid)
{
process=childProcess.get(i);
break;
}
}
if(process==null)
return -1;
process.joinLock.acquire();
process.joinCondition.sleep();
process.joinLock.release();
byte[] childstat = new byte[4];
for(int i=0;i<childProcess.size();i++) {
System.out.print(childProcess.get(i).pid+" ");
}
childstat=Lib.bytesFromInt(process.status);
int numWriteByte=writeVirtualMemory(statusAddress,childstat);
if(process.normalExit&&numWriteByte==4) {
System.out.println("join success");
return 1;}
return 0;
}
private int handleExit(int status){
coff.close();
for(int i=0;i<16;i++){
if(openfile[i]!=null)
{
openfile[i].close();
openfile[i]=null;
}
}
this.status=status;
normalExit=true;
if(parentProcess!=null){
joinLock.acquire();
joinCondition.wake();
joinLock.release();
parentProcess.childProcess.remove(this);
}
unloadSections();
if(numOfRunningProcess==1)
Machine.halt();
numOfRunningProcess--;
KThread.currentThread().finish();
System.out.println("exit success");
return 0;
}
2.4.相关代码
public int getEffectivePriority() {
// implement me
effectivepriority=priority;
for(int i=0;i<waitQueue.waitQueue.size();i++)
{
if(waitQueue.waitQueue.get(i).getEffectivePriority()>effectivepriority) {
effectivepriority=effectivepriority+waitQueue.waitQueue.get(i).getEffectivePriority();
}
}
return effectivepriority;
}
public void setPriority(int priority) {
if(this.priority == priority)
return;
this.priority = priority;
}
public KThread nextThread() {
Lib.assertTrue(Machine.interrupt().disabled());
// implement me
index=0;
int max=0;
int nownumber=0;
ThreadState temp=null;
KThread thread=null;
while((temp=pickNextThread())!=null){
max=max+temp.getEffectivePriority();
// System.out.println("现在是");
}
Random ran=new Random();
int select=ran.nextInt(max+1);
//
select=select-1;
// System.out.println("随机数是"+select);
for(int i=0;i<waitQueue.size();i++) {
nownumber=nownumber+waitQueue.get(i).getEffectivePriority();
if(nownumber>=select) {
thread=waitQueue.get(i).thread;
break;
}
}
if(thread==null)
return null;
else {
waitQueue.remove(thread.schedulingState);
return thread;
}
}