事先说明一点,在实验六开始,绝大多数的问题我应该都无法解释了,因为我自己做这个实验都是有点困难的,所以在接下来我不会过多阐述原理上的东西,只交待这个东西是怎么做的。
另外实验六七又是连在一起的因此。。。。。唉,没办法,一起写了呗
实验需求大致可以去分为如下几个步骤:
1.在APPSpace中实现两个系统调用,第一关是print方法,另一个是Exec方法,这两个方法可以实现一个用户程序启动另一个用户程序,类似linux中fork这个东西
2.在用户程序中打印一个整数数值,也就是新增一个系统调用PrintInt
3.尝试说明,如何实现类似linux下的fork函数,还有及写时复写机制的实现:
复制文件:
我们需要复制一下文件到lab6文件夹(关于这个文件夹还有一些需要处理的地方,这个我们会在第四个模块详细解释一下)
对于地址空间这个类(关于进程和地址空间的关系,这里我就不多解释了)
新增两个声明方法
在具体的类中,首先进行原有方法的修改:
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");
}
在这个类中,对唯一的方法进行重写
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);
}
}
新增两个控制的方法:
在类中实现对于这两个方法的重构:
//执行一个用户进程
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);
}
在宏定义也就是头文件中加入如下设定:
在cc文件中执行类似的操作即可
最后将线程相关的宏定义修改为:
编译以后按照指令进行调试
./nachos -x ../test/exec.noff
得到输出结果即为成功
好像没啥问题,哦对了注意一点
lab6这个文件名称最好不要改动,因为我发现不仅仅是local中用到了引用。。。
不过如果您头铁,当我另说