mirror of
https://github.com/Didnelpsun/CS408.git
synced 2026-11-02 05:13:43 +08:00
更新内存管理
This commit is contained in:
@@ -388,10 +388,6 @@ bool LinkList::Delete(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkList::Delete(int index, int length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinkListWithHead::Delete(int index, int length) {
|
||||
if (index < 1) {
|
||||
cout << "Delete:删除索引值" << index << "过小!" << endl;
|
||||
@@ -475,9 +471,9 @@ bool LinkListWithoutHead::Delete(int index, int length) {
|
||||
cout << "Delete:删除索引值" << index << "过大!" << endl;
|
||||
return false;
|
||||
}
|
||||
// 此时i==index-1,start到达,求end
|
||||
end = start;
|
||||
for (int i = 0; i < length; i++) {
|
||||
// 从1开始遍历
|
||||
end = this->GetNext();
|
||||
for (int i = 1; i < index + length - 1; i++) {
|
||||
end = end->GetNext();
|
||||
if (end == nullptr) {
|
||||
cout << "Delete:删除索引最大值" << index + length - 1 << "大于链表最大索引" << length << endl;
|
||||
@@ -485,7 +481,11 @@ bool LinkListWithoutHead::Delete(int index, int length) {
|
||||
}
|
||||
}
|
||||
if (index == 0) {
|
||||
this->SetNext(end);
|
||||
this->SetData(end->GetNext()->GetData());
|
||||
this->SetNext(end->GetNext()->GetNext());
|
||||
}
|
||||
if (index == 1) {
|
||||
this->SetNext(end->GetNext());
|
||||
}
|
||||
else {
|
||||
start->SetNext(end->GetNext());
|
||||
|
||||
@@ -19,17 +19,21 @@ int SequenceListTest() {
|
||||
}
|
||||
|
||||
int LinkListTest() {
|
||||
LinkListWithHead list;
|
||||
//cout << list.Empty() << endl;
|
||||
element_type a[6] = { '1','2','3','4','5','6' };
|
||||
list.NextInsert(a, 0, 5);
|
||||
list.Print();
|
||||
list.Delete(2, 3);
|
||||
list.Print();
|
||||
element_type a[6] = {'0', '1','2','3','4','5' };
|
||||
// LinkListWithHead list;
|
||||
// list.NextInsert(a, 0, 5);
|
||||
// list.Print();
|
||||
// list.Delete(2, 4);
|
||||
// list.Print();
|
||||
/*cout << list.GetLength() << endl;
|
||||
LinkListWithoutHead list2;
|
||||
list2.PriorInsert(a, 2, 3);
|
||||
list2.Print();
|
||||
cout << list2.GetLength() << endl;*/
|
||||
auto* list = new LinkListWithoutHead();
|
||||
list->NextInsert(a, 0 ,5);
|
||||
list->Print();
|
||||
list->Delete(2,4);
|
||||
list->Print();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,2 +1,187 @@
|
||||
# 内存管理习题
|
||||
|
||||
## 普通内存管理
|
||||
|
||||
### 内存管理基础知识
|
||||
|
||||
#### 内存管理内容
|
||||
|
||||
**例题** 下面关于存储管理的叙述中,正确的是()。
|
||||
|
||||
$A.$存储保护的目的是限制内存的分配
|
||||
|
||||
$B.$在内存为$M$、有$N$个用户的分时系统中,每个用户占用$M/N$的内存空间
|
||||
|
||||
$C.$在虚拟内存系统中,只要磁盘空间无限大,作业就能拥有任意大的编址空间
|
||||
|
||||
$D.$实现虚拟内存管理必须有相应硬件的支持
|
||||
|
||||
解:$A$限制内存的分配是存储保护,所以错误。$B$因为是分时系统,所以同一时间只有一个用户或程序,所以每个用户占有$M$的内存空间,所以错误。选项$C$中编址空间的大小取决于硬件的访存能力,一般由地址总线宽度决定。选项$D$中虚拟内存的管理需要由相关的硬件和软件支持,有请求分页页表机制、缺页中断机构、地址变换机构等。
|
||||
|
||||
**例题** 多进程在主存中彼此互不干扰的环境下运行,操作系统是通过()来实现的。
|
||||
|
||||
$A.$内存分配
|
||||
|
||||
$B.$内存保护
|
||||
|
||||
$C.$内存扩充
|
||||
|
||||
$D.$地址映射
|
||||
|
||||
解:$B$。多进程的执行通过内存保护实现互不干扰,如页式管理中有页地址越界保护,段式管理中有段地址越界保护。
|
||||
|
||||
**例题** 对主存储器的访问,()。
|
||||
|
||||
$A.$以块(即页)或段为单位
|
||||
|
||||
$B.$以字节或字为单位
|
||||
|
||||
$C.$随存储器的管理方案不同而异
|
||||
|
||||
$D.$以用户的逻辑记录为单位
|
||||
|
||||
解:$B$。注意这里指存储的访问,所以以字或字节为单位,而不是$D$。
|
||||
|
||||
**例题** 现有一个容量为$10GB$的磁盘分区,磁盘空间以簇为单位进行分配,簇的大小为$4KB$,若采用位图法管理该分区的空闲空间,即用一位标识一个簇是否被分配,则存放该位图所需的簇为()个。
|
||||
|
||||
$A.80$
|
||||
|
||||
$B.320$
|
||||
|
||||
$C.80K$
|
||||
|
||||
$D.320K$
|
||||
|
||||
解:$A$。簇的总数为$10GB/4KB=2.5M$,用一位标识一簇是否被分配,整个磁盘共需要$2.5Mb$,即需要$2.5M\div8=320KB$,因此共需要$320KB\div4KB=80$簇,选$A$。
|
||||
|
||||
#### 程序载入过程
|
||||
|
||||
**例题** 在虚拟内存管理中,地址变换机构将逻辑地址变换为物理地址,形成该逻辑地址的阶段是()。
|
||||
|
||||
$A.$编辑
|
||||
|
||||
$B.$编译
|
||||
|
||||
$C.$链接
|
||||
|
||||
$D.$装载
|
||||
|
||||
解:$C$。编译后的程序需要经过链接才能装载,而链接后形成的目标程序中的地址就是逻辑地址,所以是链接的过程中。若问的是完成逻辑地址到物理地址的变换的阶段就是装载。
|
||||
|
||||
### 内存空间分配回收
|
||||
|
||||
#### 动态分区分配
|
||||
|
||||
**例题** 在下列动态分区分配算法中,最容易产生内存碎片的是()。
|
||||
|
||||
$A.$首次适应算法
|
||||
|
||||
$B.$最坏适应算法
|
||||
|
||||
$C.$最佳适应算法
|
||||
|
||||
$D.$循环首次适应算法
|
||||
|
||||
解:$C$。最佳适应算法总是匹配与当前大小要求最接近的空闲分区,但是大多数情况下空闲分区的大小不可能完全和当前要求的大小相等,几乎每次分配内存都会产生很小的难以利用的内存块,所以最佳适应算法最容易产生最多的内存碎片。
|
||||
|
||||
#### 页式存储管理
|
||||
|
||||
**例题** 若页面大小$L$为$1KB$,页号$2$对应的内存块号$B=8$,将逻辑地址$A=2500$转换为物理地址$E$。
|
||||
|
||||
等价描述:某系统按字节寻址,逻辑地址结构中,页内偏移量占$10$位,页号$2$对应的内存块号$B=8$,将逻辑地址$A=2500$转换为物理地址$E$。
|
||||
|
||||
解:按照等价描述,若一个页面的页内偏移量占$10$位,所以页面大小需要$10$位来表示,即页面大小为$2^{10}B=1KB$。
|
||||
|
||||
第一步,计算页号和页内偏移量。页号$P=A\div L=2500\div1024=2$,页内偏移量$W=A\%L=2500\%1024=452$。
|
||||
|
||||
第二步,没有越界,其存放的内存块号为$8$。
|
||||
|
||||
第三步,物理地址$E=B\times L+W=8×1024+452=8848$。
|
||||
|
||||
#### 快表
|
||||
|
||||
**例题** 某系统使用基本分页存储管理,并采用了具有快表的地址变换机构。访问一次快表耗时$1\mu s$,访问一次内存耗时$100\mu s$。若快表的命中率为$90\%$,那么访问一个逻辑地址的平均耗时是多少?
|
||||
|
||||
解:若快表命中,则只用查找一次快表和一次内存,若快表不命中,则要查找一次快表和两次内存。
|
||||
|
||||
所以$=(1+100)\times0.9+(1+100+100)\times0.1=111\mu s$。
|
||||
|
||||
若该系统支持快表慢表同时查找,则为$(1+100)\times0.9+(100+100)\times0.1=110.9\mu s$。
|
||||
|
||||
#### 两级页表
|
||||
|
||||
**例题** 某计算机采用二级页表的分页存储管理方式,按字节编址,页大小为$2^{10}B$,页表项大小为$2B$,逻辑地址结构为(页目录号,页号,页内偏移量),逻辑地址空间大小为$2^{16}$页,则表示整个逻辑地址空间的页目录表中包含表项的个数至少是()。
|
||||
|
||||
$A.64$
|
||||
|
||||
$B.128$
|
||||
|
||||
$C.256$
|
||||
|
||||
$D.512$
|
||||
|
||||
解:因为页面大小为$2^{10}B$,而页表项大小为$2B$,所以一页可以存在$2^9$个页表项。逻辑地址空间为$2^{16}$页,所以一共需要$2^{16}\div2^9=2^7=128$个页面来保存二级页表,所以页目录表中必须包含表项的个数至少为$128$(首级页表项)。
|
||||
|
||||
**例题** 某系统按字节编址,采用$40$位逻辑地址,页面大小为$4KB$,页表项大小为$4B$,假设采用纯页式存储,则要采用()级页表,页内偏移量为()位?
|
||||
|
||||
解:因为页面大小为$4KB=2^{12}B$,按字节编址,所以页内偏移量等于页面大小$2^{12}B$。
|
||||
|
||||
页号则$=40-12=28$位。
|
||||
|
||||
页面大小$2^{12}B$,页表项大小$4B$,则每个页面可用存放$2^{12}\div4=2^{10}$个页表项。
|
||||
|
||||
所以各级页表最多包含$2^{10}$个页表项,需要$10$位二进制位才能映射到$2^{10}$个页表项,因此每一级的页表对应页号应该为$10$位,$28$位的页号至少分为三级。
|
||||
|
||||
#### 段式存储管理
|
||||
|
||||
**例题** 采用分页或分段管理后,提供给用户的物理地址空间()
|
||||
|
||||
$A.$分页支持更大的物理地址空间
|
||||
|
||||
$B.$分段支持更大的物理地址空间
|
||||
|
||||
$C.$不能确定
|
||||
|
||||
$D.$一样大
|
||||
|
||||
解:$C$。页表和段表同样存储在内存中,系统提供给用户的物理地址空间为总空间大小减去页表或段表的长度。由于页表和段表的长度不能确定,所以提供给用户的物理地址空间大小也不能确定。
|
||||
|
||||
**例题** 下面的()方法有利于程序的动态链接。
|
||||
|
||||
$A.$分段存储管理
|
||||
|
||||
$B.$分页存储管理
|
||||
|
||||
$C.$可变式分区管理
|
||||
|
||||
$D.$固定式分区管理
|
||||
|
||||
解:$A$。程序的动态链接与程序的逻辑结构相关,分段存储管理将程序按照逻辑段进行划分,因此有利于其动态链接。其他的内存管理方式与程序的逻辑结构无关。
|
||||
|
||||
**例题** 可重入程序是通过()方法来改善系统性能的。
|
||||
|
||||
$A.$改变时间片长度
|
||||
|
||||
$B.$改变用户数
|
||||
|
||||
$C.$提高对换速度
|
||||
|
||||
$D.$减少对换数量
|
||||
|
||||
解:$D$。不能被修改的代码称为纯代码或可重入代码(不属于临界资源),这样的代码是可以共享的。可修改的代码是不能共享的(比如,有一个代码段中有很多变量,各进程并发地同时访问可能造成数据不一致)。可重入程序主要是通过共享来使用同一块存储空间的,或通过动态链接的方式将所需的程序段映射到相关进程中去,其最大的优点是减少了对程序段的调入/调出,因此减少了对换数量。
|
||||
|
||||
### 内存空间扩充
|
||||
|
||||
#### 交换技术
|
||||
|
||||
**例题** 在使用交换技术时,若一个进程正在(),则不能交换出主存。
|
||||
|
||||
$A.$创建
|
||||
|
||||
$B.I/O$操作
|
||||
|
||||
$C.$处于临界段
|
||||
|
||||
$D.$死锁
|
||||
|
||||
解:$B$。交换技术就是内存紧张时把暂时用不到的程序移动到外存中。进程正在进行$I/O$操作时不能换出主存,否则其$I/O$数据区将被新换入的进程占用,导致错误。不过可以在操作系统中开辟$I/O$缓冲区,将数据从外设输入或将数据输出到外设的$I/O$活动在系统缓冲区中进行,这时系统缓冲区与外设$I/O$时,进程交换不受限制。其中比较模糊的是$C$。只有**内核临界区**不能被换出,普通临界区可以。
|
||||
|
||||
@@ -61,8 +61,8 @@
|
||||
|
||||
连续分配是指为用户进程分配的必须是一个连续的内存空间。而非连续分配反之。
|
||||
|
||||
+ 内部碎片,分配给某进程的内存区域中,如果有些部分没有用上。
|
||||
+ 外部碎片,是指内存中的某些空闲分区由于太小而难以利用。
|
||||
+ 内部碎片,是已经被分配出去(能明确指出属于哪个进程)却不能被利用的内存空间。
|
||||
+ 外部碎片,是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域。
|
||||
|
||||
#### 单一连续分配
|
||||
|
||||
@@ -165,75 +165,48 @@
|
||||
页表:
|
||||
|
||||
+ 因为允许将进程的各个页离散地存储在内存不同的物理块中,但系统应能保证进程的正确运行,即能在内存中找到每个页面所对应的物理块,所以为了能知道进程的每个页面在内存中存放的位置,操作系统要为每个进程建立一张页表,其中页表大小也与页面一样被页框约束。
|
||||
+ 一个进程对应一张页表。
|
||||
+ 进程的每一页对应一个页表项。
|
||||
+ 每个页表项由“页号”和“块号”组成。完成从逻辑的页号向物理的块号的映射。
|
||||
+ 页表记录进程页面和实际存放的内存块之间的对应关系。
|
||||
+ 一个进程对应一张页表。
|
||||
+ 进程的每一页对应一个页表项。
|
||||
+ 每个页表项由“页号”和“块号”组成。完成从逻辑的页号向物理的块号的映射。
|
||||
+ 页表记录进程页面和实际存放的内存块之间的对应关系。
|
||||
|
||||
**例题** 假设某系统物理内存大小为4GB,页面大小为4KB,则每个页表项至少应该为多少字节?
|
||||
假设某系统物理内存大小为$4GB$,页面大小为$4KB$,则每个页表项至少应该为多少字节?
|
||||
|
||||
4GB = $2^{32}$B,4KB = $2^{12}$B。
|
||||
+ 首先$4GB=2^{32}B$,$4KB=2^{12}B$。
|
||||
+ 因此$4GB$的内存总共会被分为$2^{32}\div2^{12}= 2^{20}$个内存块,因此内存块号的范围应该是$0\cdots2^{20}-1$,因此至少要$20$个二进制位才能表示这么多的内存块号,因此至少要$3$个字节才够,因为每个字节$8$位,$3\times8=24>20$。
|
||||
+ 所以最少为三个字节。
|
||||
+ 深入来看,因为每页表项会顺序连续存储在内存中,若该页表在内存中存放的起始地址是$X$,则$M$号页对应的页表项存放在内存地址$X+3\times M$。
|
||||
+ 同时因为页面大小为$4KB$,所以每个页框(即可用存放的最大值)大小为$4\times1024\div3=1365$个页表项,但是此时每页会余下$4\times1024\mod3=1$页内碎片,所以一定会在中间空出内存,所以$1365$号页在页框约束下会在新的下一个页框存储,表项会存放在$X+3\times1365+1$处。这时候地址公式就不管用了。
|
||||
+ 而如果每个页表项占$4$字节,则每个页框刚好能放下$1024$个页表项,从而没有余数,能减少查找的麻烦。
|
||||
+ 所以理论上$3B$就能表示内存块的范围,但是为了方便页表查找(对齐),实际上会多一些字节,使得每个页面能装下整数个页表项。
|
||||
|
||||
因此4GB的内存总共会被分为$2^{32}\div2^{12}= 2^{20}$个内存块,因此内存块号的范围应该是$0\cdots2^{20}-1$,因此至少要20个二进制位才能表示这么多的内存块号,因此至少要3个字节才够,因为每个字节8位,$3\times8=24$。
|
||||
页式基本地址变换机构:
|
||||
|
||||
所以最少为三个字节。
|
||||
|
||||
深入来看,因为每页表项会顺序连续存储在内存中,若该页表在内存中存放的起始地址是X,则M号页对应的页表项存放在内存地址X+3×M。
|
||||
|
||||
同时因为页面大小为4KB,所以每个页框(即可用存放的最大值)大小为4*1024÷3=1365个页表项,但是此时会余下4×1024÷3=$页内$碎片,所以一定会在中间空出$内存$,所以1365号页在页框约束下会在新的下一个页框存储,表项会存放在X+3×1365+1处。这时候地址公式就不管用了。
|
||||
|
||||
而如果每个页表项占4字节,则每个页框刚好能放下1024个页表项,从而没有余数,能减少查找的麻烦。
|
||||
|
||||
所以理论上3B就能表示内存块的范围,但是为了方便页表查找(对齐),实际上会多一些字节,使得每个页面能装下整数个页表项。
|
||||
|
||||
#### 基本地址变换机构
|
||||
|
||||
可用借助页表进行转换,通常会在系统中设置一个页表寄存器(PTR),存放页表在内存中的起始地址F和页表长度M(即这个进程里有多少页)。进程未执行时,页表的始址和页表长度放在进程控制块($PCB$)中,当进程被调度时,操作系统内核会把它们放到页表寄存器中。
|
||||
|
||||
在页式存储管理的系统中时,只用确定页面大小和逻辑结构就能得到物理地址。
|
||||
+ 可用借助页表进行转换,通常会在系统中设置一个页表寄存器($PTR$),存放页表在内存中的起始地址$F$和页表长度$M$(即这个进程里有多少页)。进程未执行时,页表的始址和页表长度放在进程控制块($PCB$)中,当进程被调度时,操作系统内核会把它们放到页表寄存器中。
|
||||
+ 在页式存储管理的系统中时,只用确定页面大小和逻辑结构就能得到物理地址。
|
||||
|
||||
基本地址变换机构需要先查询页表,再查询内存两次操作:
|
||||
|
||||
1. 要算出逻辑地址A对应的页号P与页内偏移量W。页号P=逻辑地址A÷页面长度L(取除法的整数部分)。页内偏移量W=A逻辑地址%页面长度L(取除法的余数部分)。
|
||||
2. 检测页号P是否越界。如果页号P大于等于页表长度M,则内中断(因为页号从0开始,页表长度至少为1,从而P=M页会越界)。
|
||||
3. 根据页表寄存器中的页表项地址PA=页表起始地址F+页号P×页表项长度PL,得到页表中对应的页表项,从而确定页面存放的内存块号B。
|
||||
1. 要算出逻辑地址$A$对应的页号$P$与页内偏移量$W$。页号$P$=逻辑地址$A$÷页面长度$L$(取除法的整数部分)。页内偏移量$W$=$A$逻辑地址%页面长度$L$(取除法的余数部分)。
|
||||
2. 检测页号$P$是否越界。如果页号$P$大于等于页表长度$M$,则内中断(因为页号从$0$开始,页表长度至少为$1$,从而$P=M$页会越界)。
|
||||
3. 根据页表寄存器中的页表项地址$PA$=页表起始地址$F$+页号$P$×页表项长度$PL$,得到页表中对应的页表项,从而确定页面存放的内存块号$B$。
|
||||
+ 页表长度指的是这个页表中总共有几个页表项,即总共有几个页。
|
||||
+ 页表项长度指的是每个页表顶占多大的存储空间。
|
||||
+ 页面大小指的是一个页面占多大的存储空间。
|
||||
4. 最后物理地址E=内存块号B×页面大小L+页内偏移量W(如果内存块号和业内偏移量用二进制表示,则直接拼接起来就是最终物理地址了)。
|
||||
|
||||
**例题** 若页面大小L为1K字节,页号2对应的内存块号B=8,将逻辑地址A=2500转换为物理地址E。
|
||||
|
||||
等价描述:某系统按字节寻址,逻辑地址结构中,页内偏移量占10位,页号2对应的内存块号b=8,将逻辑地址A=2500转换为物理地址E。
|
||||
|
||||
按照等价描述,若一个页面的页内偏移量占10位,所以页面大小需要10位来表示,即页面大小为$2^{10}B=1KB$。
|
||||
|
||||
第一步,计算页号和页内偏移量。页号P=A÷L=2500÷1024=2,页内偏移量W=A%L=2500%1024=452。
|
||||
|
||||
第二步,没有越界,其存放的内存块号为8。
|
||||
|
||||
第三步,物理地址E=B×L+W=8×1024+452=8848。
|
||||
4. 最后物理地址$E$=内存块号$B$×页面大小$L$+页内偏移量$W$(如果内存块号和业内偏移量用二进制表示,则直接拼接起来就是最终物理地址了)。
|
||||
|
||||
#### 具有快表的地址变换机构
|
||||
|
||||
+ 时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很可能再次被访问(因为程序中存在大量的循环)。
|
||||
+ 空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问(因为很多数据在内存中都是连续存放的)。
|
||||
+ 快表,又称相连寄存器(TLB),是一种访问速度比内存快很多的高速缓冲存储器,用来存放当前访问的若干页表项,以加速地址变换的过程。与此对应,内存中的页表常称为慢表。
|
||||
+ 快表,又称相连寄存器($TLB$),是一种访问速度比内存快很多的高速缓冲存储器,用来存放当前访问的若干页表项,以加速地址变换的过程。与此对应,内存中的页表常称为慢表。
|
||||
+ 由于查询快表的速度比查询页表的速度快很多,因此只要快表命中,就可以节省很多时间。
|
||||
+ 因为局部性原理,一般来说快表的命中率可以达到90%以上。
|
||||
+ 因为局部性原理,一般来说快表的命中率可以达到$90\%$以上。
|
||||
+ 快表的地址变换过程:
|
||||
1. $CPU$给出逻辑地址,由某个硬件算得页号、页内偏移量,将页号与快表中的所有页号进行比较。
|
||||
2. 如果找到匹配的页号(即命中),说明要访问的页表项在快表中有副本,则直接从中取出该页对应的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表命中,则访问某个逻辑地址仅需一次访存即可。
|
||||
3. 如果没有找到匹配的页号,则需要访问内存中的页表,找到对应页表项,得到页面存放的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。同时将其存入快表,以便后面可能的再次访问。但若快表已满,则必须按照一定的页面置换算法对旧的页表项进行替换。因此,若快表未命中,则访问某个逻辑地址需要两次访存。
|
||||
|
||||
**例题** 某系统使用基本分页存储管理,并采用了具有快表的地址变换机构。访问一次快表耗时1μs,访问一次内存耗时100μs。若快表的命中率为90%,那么访问一个逻辑地址的平均耗时是多少?
|
||||
|
||||
若快表命中,则只用查找一次快表和一次内存,若快表不命中,则要查找一次快表和两次内存。
|
||||
|
||||
所以(1+100)×0.9+(1+100+100)×0.1=111μs。
|
||||
|
||||
若该系统支持快表慢表同时查找,则为(1+100)×0.9+(100+100)×0.1=110.9μs。
|
||||
|
||||
#### 两级页表
|
||||
|
||||
单级页表的缺点:
|
||||
@@ -252,39 +225,32 @@
|
||||
|
||||
注意点:
|
||||
|
||||
+ 若采用多级页表机制,则各级页表的大小不能超过一个页面。因为顶级页表只能有一个,否则一个页面放不下页表项。如果一个页面框放不下就需要多个页面框,而如果需要多个页面框就会导致多个页面框有相同的页号,就不能区分出哪个是顶级页表。
|
||||
+ 若采用多级页表机制,则各级页表的大小不能超过**一个**页面。因为顶级页表只能有一个,否则一个页面放不下页表项。如果一个页面框放不下就需要多个页面框,而如果需要多个页面框就会导致多个页面框有相同的页号,就不能区分出哪个是顶级页表。
|
||||
+ 两级页表的访存次数分析(假设没有快表机构):
|
||||
1. 第一次访存:访问内存中的页目录表。
|
||||
2. 第二次访存:访问内存中的二级页表。
|
||||
3. 第三次访存:访问目标内存单元。
|
||||
|
||||
**例题** 某系统按字节编址,采用40位逻辑地址,页面大小为4KB,页表项大小为4B,假设采用纯页式存储,则要采用()级页表,页内偏移量为()位?
|
||||
|
||||
因为页面大小为4KB=$2^{12}$B,按字节编址,所以页内偏移量等于页面大小$2^{12}$B。
|
||||
|
||||
页号则=40-12=28位。
|
||||
|
||||
页面大小$2^{12}$B,页表项大小4B,则每个页面可用存放$2^{12}\div4=2^{10}$个页表项。
|
||||
|
||||
所以各级页表最多包含$2^{10}$个页表项,需要10位二进制位才能映射到$2^{10}$个页表项,因此每一级的页表对应页号应该为10位,28位的页号至少分为三级。
|
||||
|
||||
#### 基本分段存储管理
|
||||
|
||||
+ 进程的地址空间:按照程序自身的逻辑关系划分为若干个不等长的段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从0开始编址。
|
||||
+ 进程的地址空间:按照程序自身的逻辑关系划分为若干个不等长的段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从$0$开始编址。
|
||||
+ 内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。
|
||||
+ 分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量〉所组成。
|
||||
+ 分段系统的逻辑地址结构由段号(段名$S$)和段内地址(段内偏移量$W$)所组成。
|
||||
+ 段号的位数决定了每个进程最多可以分几个段段内地址位数决定了每个段的最大长度是多少。
|
||||
+ 页式系统中逻辑地址的页号和页内偏移量对用户透明,但是段式系统段号和段内偏移量必须用户显式提供,一般由编译程序完成。
|
||||
+ 程序分多个段,各段离散地装入内存,为了保证程序能正常运行,就必须能从物理内存中找到各个逻辑段的存放位置。为此,需为每个进程建立一张段映射表,简称“段表”:
|
||||
1. 每个段对应一个段表项,其中记录了该段在内存中的起始位置(又称“基址”)和段的长度。
|
||||
2. 各个段表项的长度是相同的。某系统按字节寻址,采用分段存储管理,逻辑地址结构为(段号16位,段内地址16位),因此用16位即可表示最大段长。物理内存大小为4GB(可用32位表示整个物理内存地址空间)。因此,可以让每个段表项占16+32= 48位,即6B。由于段表项长度相同,因此段号可以是隐含的,不占存储空间。若段表存放的起始地址为R,则K号段对应的段表项存放的地址为R+K*6,段号并不占内存空间。
|
||||
1. 每个段对应一个段表项,其中记录了段号、该段在内存中的起始位置(又称“基址”)和段的长度。
|
||||
2. 各个段表项的长度是相同的。
|
||||
|
||||
某系统按字节寻址,采用分段存储管理,逻辑地址结构为(段号$16$位,段内地址$16$位),因此用$16$位即可表示最大段长。物理内存大小为$4GB$(可用$32$位表示整个物理内存地址空间)。因此,可以让每个段表项占$16+32=48$位,即$6B$。由于段表项长度相同,因此段号可以是隐含的,不占存储空间。若段表存放的起始地址为$R$,则K号段对应的段表项存放的地址为$R+K\times6$,段号并不占内存空间。
|
||||
|
||||
段式存储地址变换过程:
|
||||
|
||||
1. 根据逻辑地址得到段号S和段内地址W。
|
||||
2. 根据段号S是否大于等于段表寄存器的段表长度M,判断上是否越界,若是则越界中断。(段表长度至少为1)。
|
||||
3. 查询段表寄存器中的段表,找到对应的段表项,段表项分为段长C和段基址B,段表项的存放地址SA=段表始值F+段号S×段表项长度SL。
|
||||
4. 检测段内地址W是否大于等于段长C(这也是段式存储与页式存储的最大不同,段式存储不定长),若是则越界中断。
|
||||
5. 计算得到物理地址E=段基址B+段内地址W。
|
||||
1. 根据逻辑地址得到段号$S$和段内地址$W$。
|
||||
2. 根据段号$S$是否大于等于段表寄存器的段表长度$M$,判断上是否越界,若是则越界中断。(段表长度至少为$1$)。
|
||||
3. 查询段表寄存器中的段表,找到对应的段表项,段表项分为段长$C$和段基址$B$,段表项的存放地址$SA$=段表始值$F$+段号$S$×段表项长度$SL$。
|
||||
4. 检测段内地址$W$是否大于等于段长$C$(这也是段式存储与页式存储的最大不同,段式存储不定长),若是则越界中断。
|
||||
5. 计算得到物理地址$E$=段基址$B$+段内地址$W$。
|
||||
|
||||
分段、分页管理的区别:
|
||||
|
||||
@@ -306,14 +272,12 @@
|
||||
分段管理|很方便按照逻辑模块实现信息的共享和保护|如果段长过大,为其分配很大的连续空间会很不方便。另外,段式管理会产生外部碎片
|
||||
|
||||
+ 段页式存储的过程:
|
||||
1. 将进程按逻辑模块分段,再将各段分页(如每个页面4KB)。
|
||||
1. 将进程按逻辑模块分段,再将各段分页(如每个页面$4KB$)。
|
||||
2. 再将内存空间分为大小相同的内存块/页框/页帧/物理块。
|
||||
3. 进程前将各页面分别装入各内存块中。
|
||||
+ 段页式系统逻辑地址结构:
|
||||
+ 由段号、页号、页内地址(页内偏移量)组成。
|
||||
+ 段号的位数决定了每个进程最多可以分几个段。
|
||||
+ 页号位数决定了每个段最大有多少页。
|
||||
+ 页内偏移量决定了页面大小、内存块大小是多少。
|
||||
+ 由段号$S$、页号$P$、页内地址$W$(页内偏移量)组成。段号的位数决定了每个进程最多可以分几个段。页号位数决定了每个段最大有多少页。页内偏移量决定了页面大小、内存块大小是多少。
|
||||
+ 系统含有一个段表寄存器,指出作业的段表始址和段表长度。
|
||||
+ “分段”对用户是可见的,程序员编程时需要显式地给出段号、段内地址。而将各段“分页”对用户是不可见的。系统会根据段内地址自动划分页号和页内偏移量。
|
||||
+ 因此段页式管理的地址结构是二维的。
|
||||
+ 分为段表和页表:
|
||||
@@ -323,13 +287,13 @@
|
||||
|
||||
段页式存储地址变换过程:
|
||||
|
||||
1. 根据逻辑地址得到段号S、页号P和页内偏移量W。
|
||||
2. 根据段号S是否大于等于段表寄存器的段表长度M,判断上是否越界,若是则越界中断。(段表长度至少为1)。
|
||||
3. 查询段表寄存器中的段表,找到对应的段表项。段表项分为段号S和页表长度L段基址和页表存放块号D。段表项的存放地址SA=段表始值F+段号S×段表项长度SL。
|
||||
4. 检测页号P是否大于等于页表项长度PL,若是则越界中断。
|
||||
5. 根据页表存放块号D和页号P查询页表,找到对应页表项。页表项分为页号P和内存块号B。
|
||||
6. 计算得到物理地址E=内存块号B+页内偏移量W。
|
||||
7. 也可引入快表机构,用段号和页号作为查询快表的关键字。若快表命中则仅而次访存。
|
||||
1. 根据逻辑地址得到段号$S$、页号$P$和页内偏移量$W$。
|
||||
2. 根据段号$S$是否大于等于段表寄存器的段表长度$M$,判断上是否越界,若是则越界中断。(段表长度至少为$1$)。
|
||||
3. 查询段表寄存器中的段表,找到对应的段表项。段表项分为段号$S$和页表长度$L$、段基址$F$和页表存放块号$D$。段表项的存放地址$SA$=段表始值$F$+段号$S$×段表项长度$SL$。
|
||||
4. 检测页号$P$是否大于等于页表项长度$PL$,若是则越界中断。
|
||||
5. 根据页表存放块号$D$和页号$P$查询页表,找到对应页表项。页表项分为页号$P$和内存块号$B$。
|
||||
6. 计算得到物理地址$E$=内存块号$B$+页内偏移量$W$。
|
||||
7. 也可引入快表机构,用段号和页号作为查询快表的关键字。若快表命中则仅两次访存。
|
||||
|
||||
### 内存空间扩充
|
||||
|
||||
|
||||
Reference in New Issue
Block a user