动态分区存储管理 因为代码使用C++写的,虽然自己之前自学过C++,代码看起来还是有一点困难,这篇博客就是我的课程设计,本次课程设计利用VS和MFC构造图形化界面更加直观。
1 设计任务 1.1 设计目的
●加深理解动态分区分配存储管理的过程; ● 建立描述内存分配状况的数据结构; ● 建立描述进程的数据结构; ● 使用两种方式产生进程:(a)自动产生, (b)手工输入; ● 在屏幕上显示内存的分配状况、每个进程的执行情况; ● 建立分区的分配与回收算法,支持紧凑算法; ● 时间的流逝可用下面几种方法模拟: (a) 按键盘,每按一次可认为过一个时间单位; (b) 响应WM_TIMER; ● 将一批进程的执行情况存入磁盘文件,以后可以读出并重放;
●实现动态分区分配算法: 首次适应算法 循环首次适应算法 最佳适应算法 最坏适应算法; ●加深对碎片处理方法——内存回收算法,以及紧凑算法的理解,认识其作用,并予以实现。1.2 设计内容 (1)动态分区分配是根据进程的实际需要,动态地为之分配内存空间。在实现动态分区存储管理时,将涉及到分区中所用的数据结构、分区分配算法和分区的分配和回收操作三个问题。 (2)设计一个动态分区存储管理程序,并模拟实现分区的分配和回收过程。 (3)要求所设计程序采用的分配算法必须同时包括以下四种算法:首次适应算法、循环首次适应算法、最佳适应算法、最差适应算法 (4)分区回收时必须具备能对相邻空闲分区合并。测试时请根据所定义的内存大小随机给出多组分区请求进行分配,并依据测试结果对所用各分配算法的优缺点加以分析比较。
2 总体设计 2.1 总体结构
2.2 开发环境
Visual studio 2019(简称VS) 是美国微软公司的开发工具包系列产品。VS是一个基本完整的开发工具集,它包括了整个软件生命周期中所需要的大部分工具,如UML工具、代码管控工具、集成开发环境 (IDE)等等。所写的目标代码适用于微软支持的所有平台。
微软基础类库(英语:Micorsoft Foundation Classes,简称MFC) 是微软公司提供的一个类库(class libraries),以C++的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。
3 数据结构以及算法设计:
内存分配状况 (Memory.h): 空闲分区表: (1) 描述: 在系统中设置一张空闲分区表,用于记录每个空闲分区的情况。每个 空闲分区占一个表目,表目中包括进程序号、分区始址及分区的大小等数据项。
(2) 实现:
内存分区:
1 2 3 4 5 6 7 8 9 10 struct Partition { int ID; int Address; int Size; bool Status; Partition () { ID = -1 ; } };
内存空间:
1 2 3 deque<Partition> deqMem;
内存总体设计:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 struct myNode { int Index; int Size; }; class Memory { private : const int totalSize = 100 ; const int beginIndex = 0 ; const int endIndex = 100 ; int unusedSize; int searchPtrForNF; deque<Partition> deqMem; vector<myNode> vecNode; Partition Ptn; myNode Nd; bool FirstFit (int Size, int ID) ; bool NextFit (int Size, int ID) ; bool BestFit (int Size, int ID) ; bool WorstFit (int Size, int ID) ; void CreateVec () ; void Increse () ; void Decrese () ; static bool CmpIncrese (const myNode &N1, const myNode &N2) ; static bool CmpDecrese (const myNode &N1, const myNode &N2) ; void Merge (int Index) ; const string Filelog = "MemLog" ; ofstream outFile; void getLog () ; public : Memory (); ~Memory (); bool Allocate (int Size, int algType, int ID) ; deque<Partition> getMemStatus () ; void ReleaseSpace (int ID, int Size) ; deque<Partition> Compact () ; string getFileName () ; void Distroy () ; int getMaxSize () ; };
进程 (Process.h) (1) 描述: 1)手工输入: 进程具有: 进程标识 + 进程所需空间大小 2)自动产生: 进程具有: 进程标识 + 进程所需空间大小 + 进程所需运行时间 (2) 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Process { private : int PID; int PSize; int PTime; int PselType; int nextID; public : Process (); ~Process (); void CreateProcess (int Size) ; void CreateProcess (int Size, int Time) ; int getPID () ; int getPSize () ; int getPTime () ; int getPselType () ; void ChangeSize (int ChangeSize) ; void decrease () ; };
3.1首次适应算法(First Fit) (1) 描述: FF 算法要求空闲分区表以地址递增的次序生成。在分配内存时,从首地址开始顺序查找,直至找到一个大小能满足要求的空闲分区为止;然后再按照作业的大小,从该分区中划出一块内存空间分配给请求者, 余下的空闲分区仍留在空闲表中。若从首地址直至尾地址都不能找到一个能满足要求的分区,则此次内存分配失败,返回。 (2) 特点: 1)优点: 该算法倾向于优先利用内存中低址部分的空闲分区,从而保留了高址部分的大空闲区。这给为以后到达的大作业分配大的内存空间创造了条件。 2)缺点: 低址部分不断被划分,会留下许多难以利用的、很小的空闲分区,而每次查找又都是 从低址部分开始,这无疑会增加查找可用空闲分区时的开销。 (3) 实现:
3.2循环首次适应算法(Next Fit) (1) 描述: 在为进程分配内存空间时,不再是每次都从首址开始查找,而是从上次找到的空闲分区的下一个空闲分区开始查找,直至找到一个能满足要求的空闲分区,从中划出一块与请求大小相等的内存空间分配给作业。为实现该算法,应设置一起始查寻指针,用于指示下一次起始查寻的空闲分区,并采用循环查找方式,即如果最后一个空闲分区的大小仍不能满足要求,则应返回到第一个空闲分区,比较其大小是否满足要求。找到后,应调整起始查寻指针 (2) 特点: 1) 优点: 该算法能使内存中的空闲分区分布得更均匀,从而减少了查找空闲分区时的开销。 2) 缺点: 缺乏大的空闲分区。 (3) 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 bool Memory::NextFit (int Size, int ID) { bool isSuccess = false ; int i = searchPtrForNF; int flag = searchPtrForNF; do { if ((!deqMem[i].Status) && (Size <= deqMem[i].Size)) { if (Size != deqMem[i].Size) { Ptn.Address = deqMem[i].Address + Size; Ptn.Size = deqMem[i].Size - Size; Ptn.Status = false ; deqMem.insert (deqMem.begin () + i + 1 , Ptn); searchPtrForNF = (i + 1 )%deqMem.size (); } deqMem[i].ID = ID; deqMem[i].Size = Size; deqMem[i].Status = true ; isSuccess = true ; break ; } i = (i + 1 ) % deqMem.size (); } while (i != flag); return isSuccess; }
3.3最佳适应算法 (1) 描述: 每次为作业分配内存时,总是把能满足要求、又是最小的空闲分区分配给作业,避免“大材小用”。为了加速寻找,该算法要求将所有的空闲分区按其容量以从小到大的顺序形成张一空闲分区表。这样,第一次找到的能满足要求的空闲区,必然是最佳的。 (2) 特点: 1) 优点: 孤立地看,最佳适应算法似乎是最佳的 2) 缺点: 在宏观上,因为每次分配后所切割下来的剩余部分总是最小的,这样,在存储器中会留下许多难以利用的小空闲区。 (3) 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 bool Memory::BestFit (int Size, int ID) { bool isSuccess = false ; Increse (); for (int m = 0 ; m < vecNode.size (); ++m) { int i = vecNode[m].Index; if ((!deqMem[i].Status) && (Size <= deqMem[i].Size)) { if (Size != deqMem[i].Size) { Ptn.Address = deqMem[i].Address + Size; Ptn.Size = deqMem[i].Size - Size; Ptn.Status = false ; deqMem.insert (deqMem.begin () + i + 1 , Ptn); searchPtrForNF = (i + 1 ) % deqMem.size (); } deqMem[i].ID = ID; deqMem[i].Size = Size; deqMem[i].Status = true ; isSuccess = true ; break ; } } vecNode.clear (); return isSuccess; }
3.4最坏首次适应算法 (1) 描述: 扫描整个空闲分区表或链表,总是挑选一个最大的空闲区分割给作业使用。该算法要求将所有的空闲分区按其容量以从大到小的顺序形成一空闲分区链,查找时只要看第一个分区能否满足作业要求。 (2) 特点: 1) 优点: 可使剩下的空闲区不至于太小,产生碎片的几率最小,对中、小作业 有利,同时最坏适应分配算法查找效率很高。 2) 缺点: 会使存储器中缺乏大的空闲分区 (3) 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 bool Memory::WorstFit (int Size, int ID) { bool isSuccess = false ; Decrese (); for (int m = 0 ; m < vecNode.size (); ++m) { int i = vecNode[m].Index; if ((!deqMem[i].Status) && (Size <= deqMem[i].Size)) { if (Size != deqMem[i].Size) { Ptn.Address = deqMem[i].Address + Size; Ptn.Size = deqMem[i].Size - Size; Ptn.Status = false ; deqMem.insert (deqMem.begin () + i + 1 , Ptn); searchPtrForNF = (i + 1 ) % deqMem.size (); } deqMem[i].ID = ID; deqMem[i].Size = Size; deqMem[i].Status = true ; isSuccess = true ; break ; } } vecNode.clear (); return isSuccess; }
3.5回收算法 (void ReleaseSpace(int ID, int Size)): (1) 描述: 当进程释放内存时,系统根据回收区的首址,从空闲区表中找到相应的插入点,此时可能出现以下四种情况之一:
回收区与插入点的前一个空闲分区 F1相邻接,见图 4-8(a)。此时应将回收区与插入 点的前一分区合并,不必为回收分区分配新表项,而只需修改其前一分区 F1的大小。
回收分区与插入点的后一空闲分区 F2相邻接,见图 4-8(b)。此时也可将两分区合并, 形成新的空闲分区,但用回收区的首址作为新空闲区的首址,大小为两者之和。
回收区同时与插入点的前、后两个分区邻接,见图 4-8(c)。此时将三个分区合并, 使用 F1的表项和 F1的首址,取消 F2的表项,大小为三者之和。
回收区既不与 F1邻接,又不与 F2邻接。这时应为回收区单独建立一新表项,填写 回收区的首址和大小,并根据其首址插入到空闲链中的适当位置。
(2) 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 void Memory::ReleaseSpace (int ID, int Size) { for (int i = 0 ; i < deqMem.size (); ++i) { if (deqMem[i].Status && (deqMem[i].ID == ID)) { if (Size == deqMem[i].Size) { deqMem[i].Status = false ; Merge (i); unusedSize += Size; break ; } if (Size > deqMem[i].Size) { deqMem[i].Status = false ; Merge (i); unusedSize += deqMem[i].Size; --i; continue ; } deqMem[i].Size -= Size; Ptn.Address = deqMem[i].Address + deqMem[i].Size; Ptn.Size = Size; Ptn.Status = false ; deqMem.insert (deqMem.begin () + i + 1 , Ptn); Merge (i + 1 ); unusedSize += Size; break ; } } getLog (); } void Memory::Merge (int Index) { int beginAddress = deqMem[Index].Address; int beginIndex = Index, endIndex = Index; int j = Index; int sizeTmp = 0 ; while (j >= 0 && !deqMem[j].Status) { beginAddress = deqMem[j].Address; beginIndex = j; sizeTmp += deqMem[j].Size; --j; } j = Index + 1 ; while (j < deqMem.size () && !deqMem[j].Status) { endIndex = j; sizeTmp += deqMem[j].Size; ++j; } searchPtrForNF -= (endIndex - beginIndex); deqMem.erase (deqMem.begin () + beginIndex, deqMem.begin () + endIndex + 1 ); Ptn.Address = beginAddress; Ptn.Size = sizeTmp; Ptn.Status = false ; deqMem.insert (deqMem.begin () + beginIndex, Ptn); }
3.6紧凑算法 (deque Memory::Compact()): (1) 描述: 在连续分配方式中,必须把一个系统或用户程序装入一个连续的内存空间。如果在系统中只有若干个小的分区,即使它们容量的总和大于要装入的程序,但由于这些分区不相邻 接,也无法把该程序装入内存。 若想把作业装入,可采用的一种方法是:将内存中的所有作业进行移动,使它们全都相邻接,这样,即可把原来分散的多个小分区拼接成一个大分区,这时就可把作业装入该 区。这种通过移动内存中作业的位置,以把原来多个分散的小分区拼接成一个大分区的方 法,称为“拼接”或“紧凑”,见图 4-9(b)。由于经过紧凑后的某些用户程序在内存中的位置发生了变化,此时若不对程序和数据的地址加以修改(变换),则程序必将无法执行。为此, 在每次“紧凑”后,都必须对移动了的程序或数据进行重定位。
(2) 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 deque<Partition> Memory::Compact () { deque<Partition> deqTmp; int mergeSize = 0 ; for (int i = 0 ; i < deqMem.size (); ++i) { if (!deqMem[i].Status) { mergeSize += deqMem[i].Size; } } Ptn.Address = 0 ; Ptn.Size = mergeSize; Ptn.Status = false ; deqTmp.push_back (Ptn); int deqTmpSize = 0 ; for (int i = 0 ; i < deqMem.size (); ++i) { if (deqMem[i].Status) { Ptn.ID = deqMem[i].ID; Ptn.Address = deqTmp[deqTmpSize].Address + deqTmp[deqTmpSize].Size; Ptn.Size = deqMem[i].Size; Ptn.Status = true ; deqTmp.push_back (Ptn); ++deqTmpSize; } } deqMem.clear (); deqMem = deqTmp; deqTmp.clear (); getLog (); return deqMem; }
4 系统实现 4.1执行模块实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 void CDynamicMemoryManagementDlg::OnBnClickedExecute () { if (m_operatingMode.GetCurSel () == 0 && m_ProcessID.GetCurSel () == 0 ) { CString str; m_ChangeSize.GetWindowTextW (str); if (str.IsEmpty () || 0 >= _ttoi(str)) { MessageBox (_T("尚未分配所要创建进程空间大小" ), _T("警告!" ), MB_OK); return ; } ProTmp.CreateProcess (_ttoi(str)); if (!Mem.Allocate (ProTmp.getPSize (), m_SelectAlgorithm.GetCurSel (), ProTmp.getPID ())) { MessageBox (_T("内存空间不足" ), _T("警告!" ), MB_OK); ProTmp.decrease (); return ; } deqProcess.push_back (ProTmp); InsertProcessList (ProTmp.getPID (), ProTmp.getPSize ()); mapIDtoSize.insert (make_pair (ProTmp.getPID (), ProTmp.getPSize ())); str.Format (_T("%d" ), ProTmp.getPID ()); m_ProcessID.AddString (str); } if (m_operatingMode.GetCurSel () == 0 && m_ProcessID.GetCurSel () != 0 ) { CString str; m_ChangeSize.GetWindowTextW (str); int toChangeSize = _ttoi(str); if (str.IsEmpty () || 0 == toChangeSize) { MessageBox (_T("尚未输入需要改变进程空间的大小" ), _T("警告!" ), MB_OK); return ; } CString CStringtoChangeID; ((CComboBox*)GetDlgItem (IDC_ProcessID))->GetWindowText (CStringtoChangeID); int toChangeID = _ttoi(CStringtoChangeID); if (toChangeSize < 0 ) { toChangeSize = -toChangeSize; if (mapIDtoSize[toChangeID] < toChangeSize){ MessageBox (_T("释放的空间过大" ), _T("警告!" ), MB_OK); return ; } else { if (mapIDtoSize[toChangeID] == toChangeSize){ m_ProcessID.DeleteString (m_ProcessID.FindString (0 , CStringtoChangeID)); } for (int i = 0 ; i < deqProcess.size (); ++i) { if (deqProcess[i].getPID () == toChangeID) { if (deqProcess[i].getPSize () == toChangeSize) { DeleteProcessList (toChangeID); deqProcess.erase (deqProcess.begin () + i); mapIDtoSize[toChangeID] -= toChangeSize; break ; } if (deqProcess[i].getPSize () < toChangeSize) { ChangeProcessList (toChangeID, -deqProcess[i].getPSize ()); deqProcess.erase (deqProcess.begin () + i); mapIDtoSize[toChangeID] -= deqProcess[i].getPSize (); --i; continue ; } ChangeProcessList (toChangeID, -toChangeSize); deqProcess[i].ChangeSize (-toChangeSize); mapIDtoSize[toChangeID] -= toChangeSize; break ; } } Mem.ReleaseSpace (toChangeID, toChangeSize); if (mapIDtoSize[toChangeID] == 0 ) { mapIDtoSize.erase (mapIDtoSize.find (toChangeID)); } } } else { if (!Mem.Allocate (toChangeSize, m_SelectAlgorithm.GetCurSel (), toChangeID)) { MessageBox (_T("内存空间不足" ), _T("警告!" ), MB_OK); return ; } int ans = -1 ; for (int i = 0 ; i < deqProcess.size (); ++i) { if (deqProcess[i].getPID () == toChangeID) { ans = i; break ; } } if (ans == -1 ) { MessageBox (_T("查找进程ID失败" ), _T("警告!" ), MB_OK); return ; } deqProcess[ans].ChangeSize (toChangeSize); mapIDtoSize[toChangeID] += toChangeSize; ChangeProcessList (deqProcess[ans].getPID (), toChangeSize); } } if (m_operatingMode.GetCurSel () == 1 ) { SetTimer (1 , 1500 , NULL ); int totalSize = Mem.getMaxSize (); int ChangeSize; srand ((unsigned )(time (NULL ))); if (deqProcess.size () == 0 ) { slcAlg = rand () % 4 ; m_SelectAlgorithm.SetCurSel (slcAlg); } while (true ) { ChangeSize = 1 + rand () % 100 ; if (ChangeSize > totalSize){ break ; } else totalSize -= ChangeSize; int setTime = 1 + rand () % 10 ; ProTmp.CreateProcess (ChangeSize, setTime); if (!Mem.Allocate (ProTmp.getPSize (), m_SelectAlgorithm.GetCurSel (), ProTmp.getPID ())) { ProTmp.decrease (); break ; } deqProcess.push_back (ProTmp); InsertProcessList (ProTmp.getPID (), ProTmp.getPSize (), setTime); } CString strTime; int timeTmp; int toChangeSize; for (int i = 0 ; i < m_ProcessLIST.GetItemCount (); ++i) { strTime = m_ProcessLIST.GetItemText (i, 2 ); timeTmp = _ttoi(strTime); --timeTmp; if (0 == timeTmp) { CString strID = m_ProcessLIST.GetItemText (i, 0 ); for (int j = 0 ; j < deqProcess.size (); ++j) { if (deqProcess[j].getPID () == _ttoi(strID)) { toChangeSize = deqProcess[j].getPSize (); deqProcess.erase (deqProcess.begin () + j); getLogForAuto (); } } Mem.ReleaseSpace (_ttoi(strID), toChangeSize); totalSize += toChangeSize; m_ProcessLIST.DeleteItem (i); --i; continue ; } strTime.Format (_T("%d" ), timeTmp); m_ProcessLIST.SetItemText (i, 2 , strTime); } } Display (Mem.getMemStatus ()); }
4.2 对话框模块实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 class CDynamicMemoryManagementDlg : public CDialogEx { public : CDynamicMemoryManagementDlg (CWnd* pParent = NULL ); enum { IDD = IDD_DYNAMICMEMORYMANAGEMENT_DIALOG }; protected : virtual void DoDataExchange (CDataExchange* pDX) ; protected : HICON m_hIcon; virtual BOOL OnInitDialog () ; afx_msg void OnSysCommand (UINT nID, LPARAM lParam) ; afx_msg void OnPaint () ; afx_msg HCURSOR OnQueryDragIcon () ; DECLARE_MESSAGE_MAP () public : afx_msg void OnCbnSelchangeoperatingmode () ; CComboBox m_operatingMode; CComboBox m_SelectAlgorithm; afx_msg void OnBnClickedExecute () ; private : Memory Mem; int slcAlg; ofstream outFile; const string FileProcessLog = "ProcessLog" ; public : afx_msg void OnCbnSelchangeProcessid () ; public : deque<Process> deqProcess; Process ProTmp; CComboBox m_ProcessID; void Display (deque<Partition> deqMem) ; void DisplayProcessList (deque<ProcessMemVar> deqProcessVar) ; void InsertProcessList (int ID, int Size) ; void DeleteProcessList (int ID) ; void ChangeProcessList (int ID, int Size) ; int Cbmm_ProcessIDIndex; CEdit m_ChangeSize; map<int , int > mapIDtoSize; afx_msg void OnBnClickedDefrag () ; CListCtrl m_ProcessLIST; afx_msg void OnLvnItemchangedProcesslist (NMHDR *pNMHDR, LRESULT *pResult) ; afx_msg void OnBnClickedProcessstop () ; afx_msg void OnBnClickedReplay () ; afx_msg void OnBnClickedReset () ; void OnDraw (CDC* pDC, CRect rt, int x, int y, int z) ; CButton m_ProcessStop; void CDynamicMemoryManagementDlg::InsertProcessList (int ID, int Size, int Time) ; afx_msg void OnTimer (UINT_PTR nIDEvent) ; CButton m_RePlay; void getLog () ; void getLogForAuto () ; void initListControl () ; };
4.3 进程终止按钮模块实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void CDynamicMemoryManagementDlg::OnBnClickedProcessstop () { CString toStopID; m_ProcessID.GetWindowTextW (toStopID); if (toStopID == "创建ID中..." ) { MessageBox (_T("请选中ID" ), _T("警告!" ), MB_OK); return ; } int toChangeID = _ttoi(toStopID), toChangeSize = mapIDtoSize[_ttoi(toStopID)]; for (int i = 0 ; i < deqProcess.size (); ++i) { if (deqProcess[i].getPID () == toChangeID) { if (deqProcess[i].getPSize () == toChangeSize) { DeleteProcessList (toChangeID); deqProcess.erase (deqProcess.begin () + i); mapIDtoSize.erase (mapIDtoSize.find (toChangeID)); break ; } } } m_ProcessID.DeleteString (m_ProcessID.FindString (0 , toStopID)); Mem.ReleaseSpace (toChangeID, toChangeSize); Display (Mem.getMemStatus ()); }
5 系统测试 进程手工输入: 1、创建7个进程,所需空间大小分别为:5、10、15、20、5,、10、15
2、终止进程2:
3、算法结果验证与对比:(注:总内存大小为100) (1)首次适应算法 理论分析:从“内存变化”的视图中我们可以看出来,左边有15的内存空间可以使用,右边有20的内存空间可以使用,如果此时我们再输入一个大小为7的进程并且采用首次适应算法,那这个进程应该在左边的15的内存处,而不是右边20的内存处。 实际结果:
(2)循环首次适应算法 理论分析:此时左边的空闲大小为8,如果此时再输入一个大小为9的进程,根据循环首次适应算法,它应该在右边,因为按理来说上次分配是在左边分配的,但是左边不够,只能再向后寻找,若再输入一个大小为5的进程,它应该在大小为9的进程右边,因为上次分配内存就是在这儿分配的,并且大小也够用。 实际结果:
(3)最佳适应算法 理论分析:删除若干进程以便再次输入进程验证, 若此时分配一个大小为5的进程,那就应该刚好在中间那个大小为10的空闲区域分配,因为最佳适应算法是把所有的空闲分区进行从小到大排序的。 实际结果:
(4)最坏适应算法: 理论分析:在上图的基础上进行创建大小为10的进程,此进程应该位于最右边大小为20的空闲区当中,因为最坏适应算法是将所有空闲区域从大到小排列的,并且总是把大空间最先分配出去。 实际结果:
4、相邻空闲区合并: 对上图进行终止若干进程,进行空闲区合并,终止ID为0的进程:
再终止ID为1的进程:
发现已经对空闲区进行了合并。 5、碎片整理:
6 设计小结 在使用MFC设计图形应用程序时遇到过很多问题,例如将程序界面的按钮和相关函数关联起来的方法所知甚少,不得不求助与一些博客论坛还有B站相关教学视频,学到了很多东西,总体感觉下来MFC的使用还是挺简单的,没有什么太难的地方,大多都是些知识性的东西,只要熟练掌握就可以写出来。 对于四个适应算法的代码自己写出来还是有许多BUG,通过在网上求助,以及和同学讨论之后都一一解决,在这过程中又一次理解了这四种适应算法的优点以及缺点。
参考文献 [1] 汤小丹、梁红兵、哲凤屏、汤子瀛 编著.计算机操作系统(第四版).西安:西安电子科技大学出版社,2014.05 [2] Randal E. Bryant,David R.O Hallaren 著.深入理解计算机系统(第二版).北京:机械工业出版社,2015.04 [3] 侯俊杰编著. 深入浅出MFC.松岗工业出版社,1997
7 如何运行项目? 用vs打开文件夹中的DynamicMemoryManagement.sln直接运行就可以得到结果