将整个内存空间可以划为若干个段,这种把程序内存空间按照逻辑意义划分为多个段,每段有段名,段的长度不定(如:代码段、数据段、堆栈段)的方式就是段式内存空间。
段氏程序内存空间堆栈段数据段代码段…哪如何表示段式内存空间的一个地址呢? 虚拟地址(VA) = 段基地址(BA) + 段内偏移量(S)
在32位linux系统中,每个进程独占4G的虚拟内存空间,每个进程的内存空间都是独立的,进程是隔离的。 进程需要运行在物理内存上,但物理内存有限,无法为每个进程都分配4G的物理内存空间。为了解决该问题,需要将进程的虚拟内存按需加载到物理内存上。 段式映射的缺点: 按照段式地址表示,当用到某个段时,需要将整个段加载到物理内存上,这样将占用大量的物理内存,而在小的时间片内,进程只用到其中一小段内存空间,因此将整个段的内存加载到物理内存中会导致利用率低。为了提高物理内存空间利用率,引入页式虚拟内存。
页式虚拟内存,是将程序的内存空间分成一个个的页,每个页的大小相等(如1kB、2kB、4kB等)。而对应的物理内存空间同样的按照同样大小的页来组织起来。进程页可以直接映射到物理内存页上。 页式内存地址表示: 虚拟内存地址(VA) = 页面基地址(BA)+ 页内偏移(S)= 页号(P) * 页大小(pSize) + 页内偏移(S)
在操作系统中如何管理进程的段和页,从而完成虚拟内存到物理内存的映射?
内存按照4kB大小的页进行管理,那么4GB内存可以分为:4GB/4kB = 1M个页(即需要1M个条目才能完全索引到4GB的内存空间)。这一个个索引组成了一张索引表,称为页表,页表存储内容是内存页所在的页框号(物理内存的页号),因此一条记录需要4byte长度,即需要1M * 4 = 4MB内存来存储页表。 一个进程就需要4MB内存来存放页表,如果将整个页表都存储在内存上,显然是不可取的(利用率低,而且需要大量的内存)。可以把1M个纪录分成:1M = 1024 x 1024来管理,即1k个小页表,而1个小页表里面含有1k个纪录。索引这1k个页表的索引值组成了页目录表。可以看到页目录表其实就是一个特殊的页表,内容是页表所在内存的页框号。 页目录表常驻在内存中,而页表按需加载到内存,这样就可以节约内存空间,提高物理内存利用率。可以得出:最少只用4kB(页目录表)+ 4kB(页表)= 8kB的内存空间就可以索引到4MB的内存空间(1k * 4kB)。
1. 页式地址映射过程
1、页式转换模块选择空闲模块(即页目录基地址)存储页目录表; 2、操作系统按需将进程当前需要的页表加载到内存中,同时将页表所在的页框号更新到页目录表中; 3、再根据进程需要将可用的内存页框分配给进程,从而将数据(代码,数据等)加载到内存上,并将页框号更新到页表中; 4、可以看到,线性地址的偏移量和物理内存的偏移量是相同的,映射主要是将线性地址的高20位映射到物理内存空间可用的页面上。
在操作系统中,通过段描述符表来管理各个进程的段信息。 段描述符表是由一条条描述符来构成的。而段描述符保存了哪些信息呢?
段描述符信息:可以看到段描述符(8字节)主要保存的信息有: 1、段基地址(32位); 2、段限长(20位); 3、段属性; 4、段类型; 5、访问该段所需的最小权限值; 6、是否存在内存中; … 这里的段限长为啥是20位呢? 个人理解是:段内偏移采用了页式地址来存放,一个页的大小是4kB,因此段内偏移量实际就是页号值,而页号值在0~1M-1范围内,因此段限长(偏移量最大值)只有1M,即20位表示即可。 虚拟内存地址(VA) = 段基地址(BA)+ 页内偏移(S)= 段基地址(BA)+ 页号(P) * 页大小(pSize) + 页内偏移(S)
段描述符表分为:全局描述符表(GDT),局部描述符表(LDT),中断描述符表(IDT)。
段描述符表及映射过程:1、操作系统维护了GDT/LDT表,根据进程的段选择子以及GDT/LDT表的基地址来选择一个段描述符(可以理解成根据数组首地址+索引值来选择一个数组元素); 2、从段描述符可以获知到段基地址、段限长; 3、根据段基地址和偏移量,得到线性地址:BA + W(s); 4、线性地址经过页式映射,得到物理地址。
在linux系统中,所有段的基地址都是0,段限长是1M,那么段式映射将无效,即只存在页式映射。
在操作系统中的段式地址表示:
段选择子CS(16位)偏移量(虚拟地址)(32位)