/// <summary> /// Конструктор заточен под 32 бита и 4096 размер страниц /// </summary> /// <param name="bitDepth">разрядность</param> /// <param name="hardware">железо в машине</param> /// <param name="pageSize">размер страницы</param> public OS(int bitDepth, Hardware hardware, int pageSize) { PageMaxAge = 70; this.PageSize = pageSize; BitDepth = bitDepth; this.hardware = hardware; int freePageCount = 0; for (int i = 0; i < hardware.RamSlotsNumber; i++) { freePageCount += (hardware.RAMs[i].ByteCells.Length * 4) / pageSize; // число блоков по 32 бита * 4 = число байт. Поделим на размер страницы в байтах и получим число страниц } this.FreePages = new List <int>(); for (int i = 0; i < freePageCount; i++) { FreePages.Add(i); } processes = new List <Process>(); // // Создаю директорию в которой будут храниться текстовики для каждого процесса (один процесс = один файл) // Directory.CreateDirectory(Directory.GetCurrentDirectory() + "\\Processies"); CurrentDirectoryName = Directory.GetCurrentDirectory() + "\\Processies"; }
/// <summary> /// Получает ссылку на массив BitArray[] (физическая память) и смещение в этой памяти, с которого начинается свободный блок /// памяти размером в указанное количество страниц. В случае неудачи выкидывает исключение OutOfMemoryException. /// </summary> /// <param name="indexesInFreePageList">результат метода private int[] GetFreePageIndexesInListOfPages(int pageCount)</param> /// <param name="offset">начало блока размером в 32(64) бита</param> /// <returns></returns> private ref BitArray[] GetFreePages(int[] indexesInFreePageList, out uint offset) { uint firstPageAdress = (uint)FreePages[indexesInFreePageList[0]] * (uint)PageSize; offset = firstPageAdress / (uint)(bitDepth / 8); // начало блока размером в 32(64) бита for (int i = 0; i < hardware.RAMs.Length; i++) { uint physicalEndAdress = hardware.RAMs[i].PhysicalAdress + (uint)(hardware.RAMs[i].ByteCells.Length * bitDepth / 8); if (firstPageAdress >= hardware.RAMs[i].PhysicalAdress && firstPageAdress < physicalEndAdress) { uint lastPageAdress = (uint)FreePages[indexesInFreePageList[indexesInFreePageList.Length - 1]] * (uint)PageSize; // // это смежные блоки памяти, рсположенные на одной физической плашке // if (lastPageAdress >= hardware.RAMs[i].PhysicalAdress && lastPageAdress < physicalEndAdress) { for (int j = 0; j < indexesInFreePageList.Length; j++) { FreePages.RemoveAt(indexesInFreePageList[j]); } return(ref hardware.RAMs[i].ByteCells); } } } throw new OutOfMemoryException("В физической памяти данные страницы находятся на разных физических модулях"); }
/// <summary> /// "Убивает" процесс с указанным индексом в списке процессов /// </summary> /// <param name="processIndex"></param> public void KillProcess(int pid) { int processIndex = 0; for (int i = 0; i < processes.Count; i++) { if (processes[i].PID == pid) { processIndex = i; break; } } for (int i = 0; i < processes[processIndex].PageTable.Size; i++) { // // очистил страницы // uint adress = ConvertToRealPhysicalAdress(processes[processIndex].PageTable.PageTableEntries[i].Adress); RAM ram = AppropriatePhysicalRamBlock(adress); ClearPage(ref ram.ByteCells, ram.PhysicalAdress, adress); // // добавить в список свободных страниц // if (processes[processIndex].PageTable.PageTableEntries[i].Present) { FreePages.Add(processes[processIndex].PageTable.PageTableEntries[i].Adress); } } // страницы, занимаемые таблицей тоже надо очистить uint realAdress = 0; for (int i = 0; i < hardware.RAMs.Length; i++) { bool isCorrect; realAdress = processes[processIndex].PageTable.GetRealPhysicalAdress(ref hardware.RAMs[i], out isCorrect); if (isCorrect) { break; } } // // Тут по хорошему надо бы проверку на значение isCorrect, но не буду делать (посмотрим, что получится) // int pageCount = processes[processIndex].PageTable.Size * (bitDepth / 8) / PageSize; for (int i = 0; i < pageCount; i++) { int pageNumber = ConvertRealAdressToPageNumber(realAdress); FreePages.Add(pageNumber); } processes[processIndex].PageTable.Clear(); FreePages.Sort(); File.Delete(CurrentDirectoryName + "\\" + processes[processIndex].FileName); processes.RemoveAt(processIndex); }
/// <summary> /// Создает новый процесс /// </summary> /// <param name="memory">количество требуемой памяти в байтах</param> public void CreateNewProcess(string name, int memory, int prioryty) { int pid = 0; // вычислить размер таблицы - количество страниц для храненния таблицы int pageCountForTable = PageNumberForTable(memory * 2); try { BitArray[] bits = GetFreePages(GetFreePageIndexesInListOfPages(pageCountForTable), out uint offset); PageTable pageTable = new PageTable(offset, ref bits, pageCountForTable); pid = CreateRandomPid(); // // инициализировать таблицу страниц // int pageCountToInit = memory / PageSize; if (FreePages.Count >= pageCountToInit) { for (int i = 0; i < pageCountToInit; i++) { pageTable.PageTableEntries[i].Adress = FreePages[0]; pageTable.PageTableEntries[i].Present = true; FreePages.RemoveAt(0); } } else { throw new OutOfMemoryException("Невозможно выделить минимум памяти процессу"); } processes.Add(new Process(name, memory, pid, prioryty, ref pageTable)); // // создать файл на диске, отвечающий за данный процесс // //File.Create(CurrentDirectoryName + processes[processes.Count - 1].Name + ".prc"); StreamWriter writer = new StreamWriter(CurrentDirectoryName + "\\" + processes[processes.Count - 1].FileName); int recordCount = processes[processes.Count - 1].WSClockEntryMirrors.Count; for (int i = 0; i < recordCount; i++) { WritePageToDrive(writer, processes[processes.Count - 1].PageTable.PageTableEntries[i], i); processes[processes.Count - 1].WSClockEntryMirrors[i].WrittenIntoFile = true; } writer.Close(); } catch (OutOfMemoryException e) { MessageBox.Show(e.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); } catch (Exception) { MessageBox.Show("Произошло что-то непредвиденное", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
/// <summary> /// Обрабатывает исключение PageFault алгоритмом WSClock. доработать, не использовать /// </summary> /// <param name="process">процесс, вызвавший ошибку</param> /// <param name="pageNumber">номер страницы в таблице страниц, обращение к которой вызвало ошибку</param> public void PageFaultExeptionHandler(Process process, int pageNumber) { bool memoryAllocated = false; if (process.PageTable.PageTableEntries[pageNumber].Adress == 0) { if (FreePages.Count > 0) { process.PageTable.PageTableEntries[pageNumber].Present = true; process.PageTable.PageTableEntries[pageNumber].Adress = FreePages[0]; FreePages.RemoveAt(0); process.WSClockEntryMirrors.Add(new WSClockEntryMirror(ref process.PageTable.PageTableEntries[pageNumber], process.CurrentVirtualTime, pageNumber)); memoryAllocated = true; StreamWriter writer = new StreamWriter(CurrentDirectoryName + "\\" + process.FileName, true); WritePageToDrive(writer, process.PageTable.PageTableEntries[pageNumber], pageNumber); writer.Close(); } } if (!memoryAllocated) { bool ableToRemove = false; List <int> removalCandidates = new List <int>(); int removalPage = 0; int removalPageIndex = 0; int workingSetPagesNumber = process.WSClockEntryMirrors.Count; for (int i = 0; i < workingSetPagesNumber; i++) { if (!ableToRemove) { if (process.WSClockEntryMirrors[i].PageTableEntryIndex == pageNumber) // то такую страницу в расчет не берем, т.к. к ней и обратились { process.WSClockEntryMirrors[i].Referenced = false; process.WSClockEntryMirrors[i].LastUseTime = process.CurrentVirtualTime; //continue; } else if (process.WSClockEntryMirrors[i].Referenced == true) { process.WSClockEntryMirrors[i].Referenced = false; process.WSClockEntryMirrors[i].LastUseTime = process.CurrentVirtualTime; } else if ((process.CurrentVirtualTime - process.WSClockEntryMirrors[i].LastUseTime) <= PageMaxAge) // это еще рабочий набор { removalCandidates.Add(i); } else // можно удалить { ableToRemove = true; removalPage = process.WSClockEntryMirrors[i].PageTableEntryIndex; removalPageIndex = i; break; } } else if (process.WSClockEntryMirrors[i].Referenced == true) { process.WSClockEntryMirrors[i].Referenced = false; process.WSClockEntryMirrors[i].LastUseTime = process.CurrentVirtualTime; } } if (!ableToRemove) { if (removalCandidates.Count == 0) { Random random = new Random(); do { removalPageIndex = random.Next(0, process.WSClockEntryMirrors.Count - 1); removalPage = process.WSClockEntryMirrors[removalPageIndex].PageTableEntryIndex; } while (removalPage == pageNumber); } else { for (int i = 0; i < removalCandidates.Count; i++) { if (!process.WSClockEntryMirrors[removalCandidates[i]].Modified) { ableToRemove = true; removalPage = process.WSClockEntryMirrors[removalCandidates[i]].PageTableEntryIndex; removalPageIndex = removalCandidates[i]; break; } } if (!ableToRemove) { removalPage = process.WSClockEntryMirrors[removalCandidates[0]].PageTableEntryIndex; removalPageIndex = removalCandidates[0]; } } } // если страница модифицирована, то ее данные нужно перезаписать на диск if (process.WSClockEntryMirrors[removalPageIndex].Modified == true) { // если страница записана на диск - обновляем данные if (process.WSClockEntryMirrors[removalPageIndex].WrittenIntoFile) { if (!WritePageToDrive(process.FileName, process.WSClockEntryMirrors[removalPageIndex].PageTableEntry, process.WSClockEntryMirrors[removalPageIndex].PageTableEntryIndex, true)) { throw new Exception("Дисковая операция с процессом крашнулась"); } } else // иначе просто пишем в файл { StreamWriter writer = new StreamWriter(CurrentDirectoryName + "\\" + process.FileName, true); WritePageToDrive(writer, process.WSClockEntryMirrors[removalPageIndex].PageTableEntry, process.WSClockEntryMirrors[removalPageIndex].PageTableEntryIndex); process.WSClockEntryMirrors[removalPageIndex].WrittenIntoFile = true; writer.Close(); } } // в записи "удаляемой" страницы выставляем бит отображения в 0. Больше с этой записью ничего делать не надо process.PageTable.PageTableEntries[process.WSClockEntryMirrors[removalPageIndex].PageTableEntryIndex].Present = false; // В ту запись (номер в таблице страниц), обращение к которой вызвало ошибку нужно записать адрес "удаленной страницы" // Удаляемую страницу нужно записать на диск, если нужно, и в таблице страниц выставить бит отображения в 0 int oldAdress = process.PageTable.PageTableEntries[pageNumber].Adress; // вычисляем физическое местоположение страницы, в которую будем писать новые данные uint adress = (uint)process.PageTable.PageTableEntries[removalPageIndex].Adress * (uint)PageSize; int ramBlockNumber = 0; for (int j = 0; j < hardware.RAMs.Length; j++) { uint endPhysicalAdress = hardware.RAMs[j].PhysicalAdress + (uint)(hardware.RAMs[j].ByteCells.Length * (bitDepth / 8)); if (adress >= hardware.RAMs[j].PhysicalAdress && adress < endPhysicalAdress) { ramBlockNumber = j; break; } } // начало страницы int startBitArrayBlock = (int)(adress - hardware.RAMs[ramBlockNumber].PhysicalAdress) / (bitDepth / 8); // если не удалось восстановить (нет на диске) if (!RestoreProcessPage(ref process, pageNumber, ref hardware.RAMs[ramBlockNumber].ByteCells, startBitArrayBlock, adress)) { MessageBox.Show("Данные поцесса на диске не найдены. Процесс будет убит.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); KillProcess(process.PID); } else { process.PageTable.PageTableEntries[pageNumber].Present = true; // костыль, но так нужно, чтобы адрес "удаляемой" страницы не был забыт process.PageTable.PageTableEntries[process.WSClockEntryMirrors[removalPageIndex].PageTableEntryIndex].Adress = oldAdress; } } }