diff --git a/Code/CPP-Code/head/link_list.h b/Code/CPP-Code/head/link_list.h index a249ff1..7f57886 100644 --- a/Code/CPP-Code/head/link_list.h +++ b/Code/CPP-Code/head/link_list.h @@ -63,8 +63,8 @@ public: // 后插入 bool NextInsert(element_type* elem, int start, int length); // 删除 - virtual bool Delete(int index); - virtual bool Delete(int index, int length) = 0; + element_type Delete(int index); + virtual element_type* Delete(int index, int length) = 0; }; class LinkListWithHead : public LinkList { @@ -76,7 +76,7 @@ public: // 插入 bool Insert(int index, element_type data) override; // 删除 - bool Delete(int index, int length) override; + element_type * Delete(int index, int length) override; }; class LinkListWithoutHead : public LinkList { @@ -95,7 +95,7 @@ public: // 插入 bool Insert(int index, element_type data) override; // 删除 - bool Delete(int index, int length) override; + element_type * Delete(int index, int length) override; }; bool LinkListNode::SetData(element_type data) { @@ -383,18 +383,19 @@ bool LinkList::NextInsert(element_type* elem, int start, int length) { } } -bool LinkList::Delete(int index) { - this->Delete(index, 1); - return true; +element_type LinkList::Delete(int index) { + return *(this->Delete(index, 1)); } -bool LinkListWithHead::Delete(int index, int length) { +element_type* LinkListWithHead::Delete(int index, int length) { + auto* data = (element_type*)malloc(length * sizeof(element_type)); if (index < 1) { cout << "Delete:删除索引值" << index << "过小!" << endl; - return false; + return data; } if (length < 1) { cout << "Delete:删除长度" << length << "过小!" << endl; + return data; } // 定义一个结点指针start指向当前扫描到的结点,即要删除第一的元素的前一个 LinkListNode* start; @@ -407,7 +408,7 @@ bool LinkListWithHead::Delete(int index, int length) { // 如果链表没有任何数据 if(start == nullptr) { cout << "Delete:链表为空!" << endl; - return false; + return data; } // 循环遍历到达指定索引号的单链表的结点 // 条件是当前结点的下一个不为空且索引号到达,所到达的结点一定不是空结点 @@ -423,10 +424,11 @@ bool LinkListWithHead::Delete(int index, int length) { // 此时i==index-1,start到达,求end end = start; for (int i = 0; i < length; i++) { + data[i] = end->GetData(); end = end->GetNext(); if (end == nullptr) { cout << "Delete:删除索引最大值" << index + length - 1 << "大于链表最大索引" << length << endl; - return false; + return data; } } if (index == 1) { @@ -436,13 +438,14 @@ bool LinkListWithHead::Delete(int index, int length) { start->SetNext(end->GetNext()); } this->SetLength(this->GetLength() - length); - return true; + return data; } -bool LinkListWithoutHead::Delete(int index, int length) { +element_type* LinkListWithoutHead::Delete(int index, int length) { + auto* data = (element_type*)malloc(length * sizeof(element_type)); if (index < 0) { cout << "Delete:删除索引值" << index << "过小!" << endl; - return false; + return data; } if (length < 1) { cout << "Delete:删除长度" << length << "过小!" << endl; @@ -458,8 +461,9 @@ bool LinkListWithoutHead::Delete(int index, int length) { // 如果链表没有任何数据 if (this->GetData() == NULL) { cout << "Delete:链表为空!" << endl; - return false; + return data; } + data[0] = this->GetData(); // 循环遍历到达指定索引号的单链表的结点 // 条件是当前结点的下一个不为空且索引号到达,所到达的结点一定不是空结点 while (start->GetNext() != nullptr && i < index - 1) { @@ -469,15 +473,18 @@ bool LinkListWithoutHead::Delete(int index, int length) { // 如果此时i小于index-1,表示遍历完还没有到达对应的索引 if (i < index - 1) { cout << "Delete:删除索引值" << index << "过大!" << endl; - return false; + return data; } // 从1开始遍历 end = this->GetNext(); for (int i = 1; i < index + length - 1; i++) { + if (i > index) { + data[i - index] = end->GetData(); + } end = end->GetNext(); if (end == nullptr) { cout << "Delete:删除索引最大值" << index + length - 1 << "大于链表最大索引" << length << endl; - return false; + return data; } } if (index == 0) { @@ -491,5 +498,5 @@ bool LinkListWithoutHead::Delete(int index, int length) { start->SetNext(end->GetNext()); } this->SetLength(this->GetLength() - length); - return true; + return data; } \ No newline at end of file diff --git a/Code/CPP-Code/head/sequence_list.h b/Code/CPP-Code/head/sequence_list.h index e710f6d..1bc1374 100644 --- a/Code/CPP-Code/head/sequence_list.h +++ b/Code/CPP-Code/head/sequence_list.h @@ -36,9 +36,9 @@ public: // 循环插入函数 bool LoopInsert(element_type *elem, int index, int length); // 删除函数 - bool Delete(int index, element_type &elem); + element_type Delete(int index); // 多个删除函数 - bool LoopDelete(int index, int len, element_type *elem); + element_type* LoopDelete(int index, int length); // 按位获取元素 element_type GetElem(int index) const; // 按值获取元素 @@ -249,32 +249,37 @@ bool SequenceList::LoopInsert(element_type *elem, int index, int length) { return true; } -bool SequenceList::Delete(int index, element_type &elem) { +element_type SequenceList::Delete(int index) { if (index >= this->GetLength() || index < 0) { cout << "Delete:删除索引" << index << "超过索引范围!" << endl; return false; } - elem = this->GetData(index); for (int i = index; i < this->GetLength(); i++) { this->SetData(i, this->GetData(i+1)); } this->SetLength(this->GetLength()-1); - return true; + return this->GetData(index); } -bool SequenceList::LoopDelete(int index, int length, element_type *elem) { +element_type* SequenceList::LoopDelete(int index, int length) { if (index + length > this->GetLength() || index < 0) { cout << "LoopDelete:删除索引" << index + length << "超过索引范围!" << endl; return false; } - for (int i = index; i <= this->GetLength() - length; i++) { - if (i < index + length) { - elem[i - index] = this->GetData(i); + auto* elem = (element_type*)malloc(length * sizeof(element_type)); + if (elem) { + for (int i = index; i <= this->GetLength() - length; i++) { + if (i < index + length) { + elem[i - index] = this->GetData(i); + } + this->SetData(i, this->GetData(i + length)); } - this->SetData(i, this->GetData(i + length)); + this->SetLength(this->GetLength() - length); } - this->SetLength(this->GetLength()-length); - return true; + else { + cout << "LoopDelete:申请空间失败!" << endl; + } + return elem; } element_type SequenceList::GetElem(int index) const { diff --git a/Code/CPP-Code/source/test.cpp b/Code/CPP-Code/source/test.cpp index a5148ec..065bcfa 100644 --- a/Code/CPP-Code/source/test.cpp +++ b/Code/CPP-Code/source/test.cpp @@ -8,8 +8,7 @@ int SequenceListTest() { element_type a[6] = {'1','2','3','4','5','6'}; list.LoopInsert(a, 0, 6); list.Print(); - element_type b[4]; - list.LoopDelete(1, 3, b); + element_type* b = list.LoopDelete(1, 3); list.Print(); for (int i = 0; i < 3; i++) { cout << b[i] << endl; @@ -33,7 +32,11 @@ int LinkListTest() { auto* list = new LinkListWithoutHead(); list->NextInsert(a, 0 ,5); list->Print(); - list->Delete(2,4); + int len = 3; + element_type* b = list->Delete(2, len); + for (int i = 0; i < len; i++) { + cout << b[i] << endl; + } list->Print(); return 0; } \ No newline at end of file diff --git a/Operate-System/2-memory-management-ex.md b/Operate-System/2-memory-management-ex.md index 94cda30..f99f9b8 100644 --- a/Operate-System/2-memory-management-ex.md +++ b/Operate-System/2-memory-management-ex.md @@ -42,6 +42,8 @@ $D.$以用户的逻辑记录为单位 解:$B$。注意这里指存储的访问,所以以字或字节为单位,而不是$D$。 +#### 位示图 + **例题** 现有一个容量为$10GB$的磁盘分区,磁盘空间以簇为单位进行分配,簇的大小为$4KB$,若采用位图法管理该分区的空闲空间,即用一位标识一个簇是否被分配,则存放该位图所需的簇为()个。 $A.80$ @@ -98,6 +100,44 @@ $D.$循环首次适应算法 第三步,物理地址$E=B\times L+W=8×1024+452=8848$。 +**例题** 某计算机主存按字节编址,逻辑地址和物理地址都是$32$位,页表项大小为$4B$。请回答下列问题: + +1)若使用一级页表的分页存储管理方式,逻辑地址结构为(页号$20$位,页内偏移量$12$位),则页的大小是多少字节?页表最大占用多少字节? + +2)若使用二级页表的分页存储管理方式,逻辑地址结构为(页目录号$10$位,页表索引$10$位,页内偏移量$12$位),设逻辑地址为$LA$,请分别给出其对应的页目录号和页表索引的表达式。 + +3)采用1)中的分页存储管理方式,一个代码段的起始逻辑地址为$0000\,8000H$,其长度为$8KB$,被装载到从物理地址$0090\,0000H$开始的连续主存空间中。页表从主存$0020\,0000H$开始的物理地址处连续存放。请计算出该代码段对应的两个页表项的物理地址、这两个页表项中的页框号,以及代码页面$2$的起始物理地址。 + +解: + +1)因为主存按字节编址,页内偏移量是$12$位,所以页大小为$2^{12}B=4KB$。物理空间为$32$位,需要用足够的页表项来表示,所以页表项数为$2^{32}\div2^{12}=2^{20}$,因此该一级页表最大的大小为$2^{20}\times4B=4MB$。 + +2)页目录号可表示为$(((unsigned\,int)(LA))>>22)\&0x3FF$。页表索引可表示为$(((unsigned\,int)(LA))>>12)\&0x3FF$。 + +3)首先代码段长度为$8KB$,而一页为$4KB$,所以代码段被分为两页。代码页面$1$的逻辑地址为$0000\,8000H$,根据一级页表的结构,前五位表示页号,$0000\,8H$表明其位于第$8$个页处,对应页表中的第$8$个页表项,所以第$8$个页表项的物理地址=页表始址+$8$×页表项的字节数=$0020\,0000H+8\times4=0020\,0020H$。所以第一个页面的物理地址就是$0020\,0020H$,页表项长度为$4B$,所以第二个页面的物理地址就是$0020\,0024H$。 + +程序段被装载到物理地址$0090\,0000H$,所以代码页面$1$的物理地址就是$0090\,0000H$,而一个页面$4KB=2^12B$,所以地址的后三位是页内偏移量,第二个代码页面物理地址就是倒数第四位加一$0090\,1000H$。而前五位就是页框号(逻辑地址就是页号,物理地址就是页框号),所以两个页表的页框号就是对应物理地址的前五位:$00900H$和$00901H$。 + +**例题** 页式存储管理允许用户的编程空间为$32$个页面(每页$1KB$),主存为$16KB$。如有一用户程序为$10$页长,且某时刻该用户程序页表见表。 + +逻辑页号|物理块号 +:------:|:------: +0|8 +1|7 +2|4 +3|10 + +若分别遇到三个逻辑地址$0AC5H$,$1AC5H$,$3AC5H$处的操作,计算并说明存储管理系统将如何处理。 + +解:首先对存储地址结构进行分析。逻辑页面$32=2^5$,而每页$1KB$,所以逻辑地址一共为$32\times1KB=2^{15}$,即一共$15$位。而此时主存为$16KB=2^{14}B$,只有$16$个页面,即物理地址一共只有$14$位,逻辑地址和物理地址不等长。而页号即页内偏移量不变,所以低$10$位是页内偏移量,逻辑地址高$5$位是虚页号,物理地址高$4$位是物理块号。 + +逻辑地址$OAC5H$转换为二进制是$000\,1010\,1100\,0101B$,虚页号为$2=00010B$,映射至物理块号$4$,因此系统访问物理地址$12C5H=01\,0010\,1100\,0101B$。 + +逻辑地址$1AC5H$转换为二进制是$001\,1010\,1100\,0101B$,虚页号为$6=00110B$,不在页面映射表中,会产生缺页中断,系统进行缺页中断处理。 + +逻辑地址$3AC5H$转换为二进制是$011\,1010\,1100 0101B$,页号为$14$,而该用户程序只有$10$页,因此系统产生越界中断。 + + #### 快表 **例题** 某系统使用基本分页存储管理,并采用了具有快表的地址变换机构。访问一次快表耗时$1\mu s$,访问一次内存耗时$100\mu s$。若快表的命中率为$90\%$,那么访问一个逻辑地址的平均耗时是多少? @@ -108,7 +148,7 @@ $D.$循环首次适应算法 若该系统支持快表慢表同时查找,则为$(1+100)\times0.9+(100+100)\times0.1=110.9\mu s$。 -#### 两级页表 +#### 多级页表 **例题** 某计算机采用二级页表的分页存储管理方式,按字节编址,页大小为$2^{10}B$,页表项大小为$2B$,逻辑地址结构为(页目录号,页号,页内偏移量),逻辑地址空间大小为$2^{16}$页,则表示整个逻辑地址空间的页目录表中包含表项的个数至少是()。 @@ -132,6 +172,18 @@ $D.512$ 所以各级页表最多包含$2^{10}$个页表项,需要$10$位二进制位才能映射到$2^{10}$个页表项,因此每一级的页表对应页号应该为$10$位,$28$位的页号至少分为三级。 +**例题** 已知系统为$32$位实地址,采用$48$位虚拟地址,页面大小为$4KB$,页表项大小为$8B$。假设系统使用纯页式存储,则要采用()级页表。 + +$A.1$ + +$B.2$ + +$C.3$ + +$D.4$ + +解:$D$。页面大小为$4KB=2^{12}B$,因此页内偏移为$12$位。系统采用$48$位虚拟地址,因此虚页号$48-12=36$位。采用多级页表时,最高级页表项不能超出一页大小。每页能容纳的页表项数为$4KB\div8B=512=2^9$,$36\div9=4$,因此应采用四级页表,最高级页表项正好占据一页空间。$32$位实地址这里是无用条件,因为实地址与物理地址相关,而页表关注的是逻辑地址。 + #### 段式存储管理 **例题** 采用分页或分段管理后,提供给用户的物理地址空间() @@ -185,3 +237,61 @@ $C.$处于临界段 $D.$死锁 解:$B$。交换技术就是内存紧张时把暂时用不到的程序移动到外存中。进程正在进行$I/O$操作时不能换出主存,否则其$I/O$数据区将被新换入的进程占用,导致错误。不过可以在操作系统中开辟$I/O$缓冲区,将数据从外设输入或将数据输出到外设的$I/O$活动在系统缓冲区中进行,这时系统缓冲区与外设$I/O$时,进程交换不受限制。其中比较模糊的是$C$。只有**内核临界区**不能被换出,普通临界区可以。 + +## 虚拟内存管理 + +### 虚拟内存的基本概念 + +### 请求分页管理方式 + +#### 缺页中断机构 + +**例题** 若用户进程访问内存时产生缺页,则下列选项中,操作系统可能执行的操作是()。 + +Ⅰ.处理越界错 + +Ⅱ.置换页 + +Ⅲ.分配内存 + +$A.$仅Ⅰ、Ⅱ + +$B.$仅Ⅱ、Ⅲ + +$C.$仅Ⅰ、Ⅲ + +$D.$Ⅰ、Ⅱ和Ⅲ + +解:$B$。用户进程访问内存时缺页,会发生缺页中断。发生缺页中断时,系统执行的操作可能是置换页面或分配内存。越界错误是编程时访问内存越界,系统内没有越界错误,不会进行越界出错处理。 + +### 页面置换算法 + +**例题** 考虑页面置换算法,系统有$m$个物理块供调度,初始时全空,页面引用串长度为$p$,包含了$n$个不同的页号,无论用什么算法,缺页次数不会少于()。 + +$A.m$ + +$B.p$ + +$C.n$ + +$D.\min(m,n)$ + +解:$C$。这个题目是对页面置换的基本概念,即最少要置换多少次。首先页面引用串就代表一共要调入$p$个页面,要调入$p$个页面,假设最坏情况所有页面都缺页,就得到缺页的最大值为$p$,所以$p$不可能是缺页的最小值。包含了$n$个不同的页号,所以就一定会缺$n$次页,所以最小值是$n$,即缺页次数一定$\geqslant n$。而对于$m$而言,若$n>m$,则由于初始为空,所以次数$>m$,综合$>n$,若$n