Nachos 用户进程地址分配
Nachos 用戶進程地址分配
遇到的問題
在實現系統調用exec的時候,出現了如下BUG
如果使用nachos 運行 exec.noff 里面 使用了Exec系統調用,執行了halt.noff程序,
出現如下錯誤
Unexpected user mode exception which = 5 type = 101 Assertion failed: line 152, file "exception.cc" Aborted (core dumped)找錯
根據輸出的which,which代表ExceptionType,即異常處理的Type,
enum ExceptionType { NoException, // Everything ok!SyscallException, // A program executed a system call.PageFaultException, // No valid translation foundReadOnlyException, // Write attempted to page marked // "read-only"BusErrorException, // Translation resulted in an // invalid physical addressAddressErrorException, // Unaligned reference or one that// was beyond the end of the// address spaceOverflowException, // Integer overflow in add or sub.IllegalInstrException, // Unimplemented or reserved instr.NumExceptionTypes };可以看出which = 5 表示是AddressErrorException,Unaligned reference or one that was beyond the end of the address space.
很有可能是地址空間的錯誤,于是看用戶進程分配地址空間的函數
AddrSpace::AddrSpace(OpenFile *executable) {// allocate pidspaceId = PidMap->Find() + 100; // 100 ---256 is userif(spaceId == 99 || spaceId > 256){printf("Process is Too Much!\n");return;}NoffHeader noffH;unsigned int i, size;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 stacknumPages = 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 memoryDEBUG('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 now, virtual page # = phys page #pageTable[i].physicalPage = PageBitmap->Find(); // Find Empty Page to ProcesspageTable[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 segmentbzero(machine->mainMemory, size);// then, copy in the code and data segments into memory// 將代碼段和數據段載入物理內存if (noffH.code.size > 0) {int pagePos = pageTable[noffH.code.virtualAddr/PageSize].physicalPage * PageSize;int offSet = noffH.code.virtualAddr % PageSize;executable->ReadAt(&(machine->mainMemory[pagePos+offSet]),noffH.code.size, noffH.code.inFileAddr); }if (noffH.initData.size > 0) {int pagePos = pageTable[noffH.initData.virtualAddr/PageSize].physicalPage * PageSize;int offSet = noffH.initData.virtualAddr % PageSize;executable->ReadAt(&(machine->mainMemory[pagePos+offSet]),noffH.initData.size, noffH.initData.inFileAddr);} }問題在哪呢?
當我把
bzero(machine->mainMemory, size);注釋掉的時候發現,一切正常,問題正在這個地方。
或者說,這個清空的操作是針對于原來每次從第0個頁分配,也就是支持一個用戶進程的時候,每次都從內存的最低地址開始清空。
但是現在是 支持多個進程,每個進程在內存中有不同的物理空間,所以要是每次從內存的首位地址開始bzero就會導致 給后面用戶線程分配內存空間時,把前面的內存空間給清空了,可能就會導致指令的錯誤,從而出現地址異常。
解決
單單注釋掉bzero雖然表面上解決了問題,但是卻不符合常理,按理說就是應該把要加載進來地址空間清空,然后讀入新的,不然如果新加載的size < 原來地址空間的size,可能后面頁里面的內碎片會影響當前進程,所以怎么辦呢?
一種解決方案,針對該過程,后面需要讀入代碼段和數據段,所以我們至少需要把代碼段和數據段的物理內存對應的地址空間清空
修改為:
// 將代碼段和數據段載入物理內存if (noffH.code.size > 0) {int pagePos = pageTable[noffH.code.virtualAddr/PageSize].physicalPage * PageSize;int offSet = noffH.code.virtualAddr % PageSize;bzero(&(machine->mainMemory[pagePos+offSet]), noffH.code.size);executable->ReadAt(&(machine->mainMemory[pagePos+offSet]),noffH.code.size, noffH.code.inFileAddr); }if (noffH.initData.size > 0) {int pagePos = pageTable[noffH.initData.virtualAddr/PageSize].physicalPage * PageSize;int offSet = noffH.initData.virtualAddr % PageSize;bzero(&(machine->mainMemory[pagePos+offSet]), noffH.initData.size);executable->ReadAt(&(machine->mainMemory[pagePos+offSet]),noffH.initData.size, noffH.initData.inFileAddr);}但是 還有Segment uninitData; 未初始化的數據段和用戶??臻g呢?
進一步修改(Shout out to WB)
即 每次分配物理頁表 把改物理頁表清空就OK了~
for (i = 0; i < numPages; i++) {pageTable[i].virtualPage = i; // for now, virtual page # = phys page #pageTable[i].physicalPage = PageBitmap->Find(); // Find Empty Page to ProcesspageTable[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-onlybzero(&(machine->mainMemory[pageTable[i].physicalPage * PageSize]), PageSize);}if (noffH.code.size > 0) {int pagePos = pageTable[noffH.code.virtualAddr/PageSize].physicalPage * PageSize;int offSet = noffH.code.virtualAddr % PageSize;//bzero(&(machine->mainMemory[pagePos+offSet]), noffH.code.size);executable->ReadAt(&(machine->mainMemory[pagePos+offSet]),noffH.code.size, noffH.code.inFileAddr); }if (noffH.initData.size > 0) {int pagePos = pageTable[noffH.initData.virtualAddr/PageSize].physicalPage * PageSize;int offSet = noffH.initData.virtualAddr % PageSize;//bzero(&(machine->mainMemory[pagePos+offSet]), noffH.initData.size);executable->ReadAt(&(machine->mainMemory[pagePos+offSet]),noffH.initData.size, noffH.initData.inFileAddr);}解決嘍
總結
以上是生活随笔為你收集整理的Nachos 用户进程地址分配的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 08-Flutter移动电商实战-dio
- 下一篇: 极域电子教室破解!