[山东大学操作系统课程设计]实验六

2023-12-13 14:38:48

0.写在前面:

事先说明一点,在实验六开始,绝大多数的问题我应该都无法解释了,因为我自己做这个实验都是有点困难的,所以在接下来我不会过多阐述原理上的东西,只交待这个东西是怎么做的。

另外实验六七又是连在一起的因此。。。。。唉,没办法,一起写了呗

1.实验需求分析:

实验需求大致可以去分为如下几个步骤:

1.在APPSpace中实现两个系统调用,第一关是print方法,另一个是Exec方法,这两个方法可以实现一个用户程序启动另一个用户程序,类似linux中fork这个东西

2.在用户程序中打印一个整数数值,也就是新增一个系统调用PrintInt

3.尝试说明,如何实现类似linux下的fork函数,还有及写时复写机制的实现:

2.实验具体操作步骤:

复制文件:

我们需要复制一下文件到lab6文件夹(关于这个文件夹还有一些需要处理的地方,这个我们会在第四个模块详细解释一下)

addrspace:

对于地址空间这个类(关于进程和地址空间的关系,这里我就不多解释了)

新增两个声明方法

在具体的类中,首先进行原有方法的修改:

AddrSpace::AddrSpace(OpenFile *executable)
{
    NoffHeader noffH;
    unsigned int i, size;

    bool flag = false;                        //将物理页面分配给进程使用
    for(i = 0; i < NumPhysPages; i++) {
        if(!ProgMap[i]) {
            ProgMap[i] = true;
            flag = true;                    //成功找到可分配的物理页面,即可完成分配
            spaceID = i;                 
            break;
        }
    }
    ASSERT(flag);                          //断言检查是否存在可用的物理页面
    //如果没有分配到,则进行输出返回

    executable->ReadAt((char *)&noffH, sizeof(noffH), 0);
    if ((noffH.noffMagic != NOFFMAGIC) && 
		(WordToHost(noffH.noffMagic) == NOFFMAGIC))
    	SwapHeader(&noffH);
    ASSERT(noffH.noffMagic == NOFFMAGIC);

// how big is address space?
    size = noffH.code.size + noffH.initData.size + noffH.uninitData.size 
			+ UserStackSize;	// we need to increase the size
						// to leave room for the stack
    numPages = divRoundUp(size, PageSize);
    size = numPages * PageSize;

    ASSERT(numPages <= NumPhysPages);		// check we're not trying
						// to run anything too big --
						// at least until we have
						// virtual memory

    DEBUG('a', "Initializing address space, num pages %d, size %d\n", 
					numPages, size);
// first, set up the translation 
    pageTable = new TranslationEntry[numPages];
    for (i = 0; i < numPages; i++) {
        pageTable[i].virtualPage = i;	// for TLB only
        pageTable[i].physicalPage = freeMM_Map->Find();
        pageTable[i].valid = TRUE;
        pageTable[i].use = FALSE;
        pageTable[i].dirty = FALSE;
        pageTable[i].readOnly = FALSE;  // if the code segment was entirely on 
					// a separate page, we could set its 
					// pages to be read-only
    }
    
// zero out the entire address space, to zero the unitialized data segment 
// and the stack segment
    bzero(machine->mainMemory, size);

// then, copy in the code and data segments into memory
    if (noffH.code.size > 0) {
        DEBUG('a', "Initializing code segment, at 0x%x, size %d\n", 
			noffH.code.virtualAddr, noffH.code.size);
    	unsigned int code_page = noffH.code.virtualAddr / PageSize; //寻找开始页面
    	unsigned int code_phy_addr = pageTable[code_page].physicalPage * PageSize;//使用页面和补偿计算物理地址
    	executable->ReadAt(&(machine->mainMemory[code_phy_addr]), noffH.code.size, noffH.code.inFileAddr);//获取存储
    }

    if (noffH.initData.size > 0) {
        DEBUG('a', "Initializing data segment, at 0x%x, size %d\n", 
			noffH.initData.virtualAddr, noffH.initData.size);
    	unsigned int data_page = noffH.initData.virtualAddr / PageSize;//寻找数据起点页面
    	unsigned int data_offset = noffH.initData.virtualAddr % PageSize;//第一个数据补偿的起点界面
    	unsigned int data_phy_addr = pageTable[data_page].physicalPage * PageSize + data_offset;  //和上一个分支中的代码一样
        executable->ReadAt(&(machine->mainMemory[data_phy_addr]), noffH.initData.size, noffH.initData.inFileAddr);//同上
    }

    Print();
}

//----------------------------------------------------------------------
// AddrSpace::~AddrSpace
// 	Dealloate an address space.
//----------------------------------------------------------------------

AddrSpace::~AddrSpace()
{
    ProgMap[spaceID] = 0;       //在解析构造方法中作出一定的修改
    for (unsigned int i = 0; i < numPages; i++)   //清理对应的内存
         freeMM_Map->Clear(pageTable[i].physicalPage);

    delete [] pageTable;
}

实现print方法,addrspace这个类就基本结束工作

void AddrSpace::Print(void)       //实现了print方法
{
	printf("spaceID: %u\n", spaceID);
    printf("Page table dump: %d pages in total\n", numPages);
    printf("============================================\n");
    printf("VirtPage, PhysPage\n");
    for (unsigned int i = 0; i < numPages; i++)
        printf("     %3d,      %3d\n", pageTable[i].virtualPage, pageTable[i].physicalPage);
    printf("============================================\n\n");
}

exception:

在这个类中,对唯一的方法进行重写

void
ExceptionHandler(ExceptionType which)       //该方法根据不同的异常情况完成重写
{
    int type = machine->ReadRegister(2);

    if (which == SyscallException) {
        switch (type) {	
            case SC_Halt:
                DEBUG('a', "Shutdown, initiated by user program.\n");
                interrupt->Halt();
                break;
            case SC_Exec:
                interrupt->Exec();
                machine->WriteRegister(PrevPCReg, machine->ReadRegister(PCReg));   //虽然调用不一样但是底下的逻辑是差不多的
	            machine->WriteRegister(PCReg, machine->ReadRegister(NextPCReg)); 
	            machine->WriteRegister(NextPCReg, machine->ReadRegister(NextPCReg) + 4); 
                break;
            
            default:
                printf("Syscall %d not implemented\n", type);
                machine->WriteRegister(PrevPCReg, machine->ReadRegister(PCReg)); 
	            machine->WriteRegister(PCReg, machine->ReadRegister(NextPCReg)); 
	            machine->WriteRegister(NextPCReg, machine->ReadRegister(NextPCReg) + 4); 
                break;
        }
    }
    else {
        printf("Unexpected user mode exception %d %d\n", which, type);
        ASSERT(FALSE);
    }
}

interrupt:

新增两个控制的方法:

在类中实现对于这两个方法的重构:

//执行一个用户进程

void Interrupt::Exec(void)
{
    printf("Execute system call of Exec()\n");

    // read argument
    char fileName[50];
    int addr = machine->ReadRegister(4);
    int i = 0;

    do {
       machine->ReadMem(addr + i, 1, (int*) &fileName[i]); // read filename from mainMemory
    } while(fileName[i++] != '\0');

    printf("Exec(%s):\n", fileName); 

    OpenFile *executable = fileSystem->Open(fileName);
    if (executable == NULL) {
        printf("Unable to open file %s to execute\n", fileName);
        return;
    }

    AddrSpace *space = new AddrSpace(executable);    
    delete executable;			// close execute file

    Thread *thread = new Thread(fileName);  // New thread for another user program
    thread->space = space;

    thread->Fork(RunProcess, (int)space->getSpaceID());
    currentThread->Yield();

    machine->WriteRegister(2, (int)space->getSpaceID());  // Return SpaceId
}


//打印并且获得一个整数

void Interrupt::PrintInt(int v)
{
    printf("%d\n", v);
}

system:

在宏定义也就是头文件中加入如下设定:

在cc文件中执行类似的操作即可

main:

最后将线程相关的宏定义修改为:

3.实验的测试工作:

编译以后按照指令进行调试

./nachos -x ../test/exec.noff

得到输出结果即为成功

4.遇到的问题:

好像没啥问题,哦对了注意一点

lab6这个文件名称最好不要改动,因为我发现不仅仅是local中用到了引用。。。

不过如果您头铁,当我另说

文章来源:https://blog.csdn.net/weixin_62697030/article/details/134921404
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。