4.5.3 分段式虚拟存储系统
分段式虚拟存储系统把作业的所有分段的副本都存放在辅助存储器中,当作业被调度投入运行时,首先把当前需要的一段或几段装入主存,在执行过程中访问到不在主存的段时再把它们装入。
段式虚拟存储管理的段表扩展段号 扩充位 主存始址特征 存取权限 辅存始址标志 限长
l 特征位,00(不在内存 ); 01(在内存 );
11(共享段 );
l 存取权限,00(可执行 ); 01(可读 ); 11(可写 );
l 扩充位,0(固定长 ); 1(可扩充 );
l 标志位,00(未修改 ); 01(已修改 ); 11(不可移动 );
S段在内存否是
B<S段长度发越界中断是否形成绝对地址继续执行指令移动或调出分段
S段末端相邻的空闲区长度满足要求地址错S段可扩充是 装入 S段重新启动指令调整 S段段表及主存分配表操作系统硬件否符合存取权限发保护中断是否发缺段中断非法存取否主存中有满足 S段长度的连续空闲区是否是移动或调出分段
4.5.4段页式存储管理
段式存储是基于用户程序结构的存储管理技术,有利于模块化程序设计,便于段的扩充、动态链接、共享和保护,但往往会生成段内碎片浪费存储空间 ;
页式存储是基于系统存储器结构的存储管理技术,存储利用率高,便于系统管理,
但不易实现存储共享、保护和动态扩充。
如果把两者优点结合起来,在分页式存储管理的基础上实现分段式存储管理这就是段页式存储管理,
1,虚地址以程序的逻辑结构划分成段,这是段页式存储管理的段式特征 。
2,实地址划分成位置固定,大小相等的页框 (块 ),
这是段页式存储管理的页式特征
3,将每一段的线性地址空间划分成与页框大小相等的页面,于是形成了段页式存储管理的特征
段号 (s) 段内页号 (p) 页内位移 (d)
4,逻辑地址形式为,
对于用户来说,段式虚拟地址应该由段号 s和段内位移 d’组成,操作系统内部再自动把 d’解释成两部分:段内页号 p和页内位移 d,也就是说,d’=p×
块长 +d。
段页式存储管理的基本原理。
段号 (s) 段内页号 (p) 页内位移 (d)
数据结构
段页式存储管理的数据结构包括作业表,
段表和页表三级结构 。
作业表中登记了进入系统中的所有作业及该作业段表的起始地址,
段表中至少包含这个段是否在内存,以及该段页表的起始地址,
页表中包含了该页是否在主存 (中断位 )、
对应主存块号 。
动态地址转换
从逻辑地址出发,先以段号 s和页号 p作索引去查快表,如果找到,那么立即获得页 p的页框号 p’,并与位移 d一起拼装得到访问主存的实地址,从而完成了地址转换 。 若查快表失败,就要通过段表和页表来作地址转换了,用段号 s作索引,找到相应表目,由此得到 s段的页表的起始地址 s’,再以 p作索引得到 s段 p页对应的表目,由此得到页框号 p’;这时一方面把 s段 p页和页框号 p’置换进快表,
另一方面用 p’和 d生成主存的实地址,从而完成地址转换 。
段表控制寄存器段表始址 段表长度 段号 s 页号 p 位移 d
段超长?
页框号 位移段表 页表
4.6实例研究,Intel Pentium
Intel的 Pentium和 Pentium Pro既可以作为分段系统,又可以作为分页系统,也可以作为一个段页式存储管理系统来运行
4.6.1Pentium虚拟存储器的核心数据结构 ——描述符表
局部描述符表 LDT(local descriptor table)
和全局描述符表 GDT(global descriptor
table)。
每个进程都有一个自己的 LDT,它描述局部于每个进程的段,包括代码段,数据段,堆栈段的基地,大小和有关控制信息等 ;
系统的所有进程共享一个 GDT,它描述系统段,包括操作系统自己的基地,大小和有关控制信息等 。
4.6.2段选择符和段描述符
选择符是一个 16位数,如图 4-32所示。
选择符中的一位指出这个段是局部的还是全局的,其他 13位是 LDT和 GDT的入口号。
索引
13 1 2
0=GDT / 1=LDT 特权级( 0-3)
l基地址共 32位 ( 分三处合并 ),生成内存段的首址,加上 32
位偏移形成内存地址 。 对于 286程序,基地址的 24—31位不用,
恒为 0;所以,286只能处理 24位基址 。
l长度位共 20位,限定段描述符寻址的内存段的长度,注意段长度的计量单位可以是字节或页 。
lG位用于描述颗粒大小,即段长度的计量单位 。 G=0表示长度以字节为单位; G=1表示长度以页为单位,在 Pentium中页的长度是固定的,为 4KB 。 于 是 段 的 长 度分 别 为 220 字节或
220?4KB=232字节 。
lD位:当 D=1时,为 32位段;当 D=0时,为 16位段 。
lP位表示内存段是否在物理主存中,若 P=1,表示段在内存中,
若 P=0,表示段不在内存中 。
DPL
基址 0-15 长度 0-15
基址 24-31 基址 16-23G D 0 长度 16-19 P S type A
lDpl位 ( 2位 ) 表示特权级 ( 0—3),用于保护 。 0为内核级; 1为系统调用级,2为共享库级,3为用户程序级 。
Windows 95只用两级,0级和 3级,即系统级和用户级 。
lS位为段位,当 S=1时,表示当前段为应用程序;当
S=0时,表示描述符将引用内存段外的系统信息 ( 如 OS
的特别数据结构 ) 。
ltype类型字段 ( 3位 ),表示内存段类型,如可执行代码段,只读数据段,调用门等等 。
l A位为访问位,表示是否访问过内存段,为淘汰作准备
4.6.3 虚拟存储运行模式选择
当设置 G控制位 1,系统运行于 段页模式,此时每个进程可有 8192个段,每个段 220个页,
每个页 4KB,也就是说段可有 232字节大小 。
当设置 G控制位 1,把系统六个段寄存器设置为同一个段选择符,段描述符基地址设置为,0”,段大小设置为最大 ;这时系统运行于 分页模式,单段,分页,32位地址空间模式运行 。
当设置 G控制位 0,系统运行于 分段模式,每个进程可有 213=8K(8192)个段,每个段可有
220字节大小,这种模式是为了与 286兼容 。
4.6.4地址转换
Pentium采用分段模式时,每一个虚地址包括一个 16位段选择符和一个 32位偏移量,不分段时,用户的虚拟内存是 232=4GB;
而分段时,虚拟地址空间为 2(32+14)=64TB;
物理地址空间使用 32个地址位,最大有
4GB。
地址转换过程如下,
l 首先根据段选择符第二位 T选择是查找
LDT或 GDT;
l 以段选择符索引域的值为索引,把它拷贝进一个内部寄存器中并且它的低三位被清零,
然后 LDT或 GDT表的起始地址被加到它上面,
找到所要访问的段描述符 。
l 检查段描述符中的 P控制位,如果段不在主存 ( 选择符为 0),就会发生一次陷入 ( 中断 )
处理;如果段已经被换出,就会发生一次陷入
( 缺段中断 ) 处理 ;如果段在主存,则随后检查偏移量是否超出了段的结尾,如果是也会发生一次陷入 ( 越界中断 ) 处理 。
l 假设段在内存中并且偏移量也在范围内,
就把描述符中 32位的基地址和偏移量相加形成 32位线性地址 ( linear address),如图 4-34
所示 。 为了和只有 24位基地址的保护模式
( 80286) 以及使用 16位段寄存器来描述 20位基地址的实模式 ( 8086/8088) 兼容,基址被分成 3片分布在描述符的各个位置 。 实际上,
基址允许各个段的起始地址在 32位线性地址空间的任何位置 。 至此段转换己经完成 。
描述符选择符 偏移量基地址长度其他的域线性地址?
是否要分页依据 G控制位,若为 0,说明目前运行于分段模式,那么段转换得到的线性地址就是物理地址并被送往存储器用于读写 ;因此在分页被禁止时,就得到了一个纯的分段解决方案,各个段的基址在它的描述符中 。 注意,
段允许互相覆盖,这是因为用硬件来检查所有的段都互不重叠代价太大,完全可以通过操作系统的软件机制加以解决 。
若 G控制位为 1,这时以分页模式运行,那么段转换得到的线性地址将被解释成分页模式的虚地址,需要通过页表找到页框后才映射成物理地址 。 这里真正复杂的是在 32位虚地址和 4K页面的情况下,一个段可能包括多达一百万个页 。 因此 Pentium使用了一种两级页表以在段较小的情况下减少页表的尺寸 。
4.6.5 二级页表和页式地址转换每个运行进程都有一个由 1024个 32位表项组成的页目录 ( page directory),它的地址由一个全局寄存器指出 。 目录中的每一个表项都指向一个也包含 1024个 32位表项的页表,页表项指向页框 ( 块 ),这个方案如图 4-35和图 4-36所示 。 线性地址被分成 3个域,Dir,Page和 Offset。
Dir域被作为索引在页目录找到指向正确页表的指针;随后,Page域被作为索引在页表找到页框的物理地址;最后,Offset被加到页框的地址上得到需要的字节或字的物理地址 。
允许分页时线性地址的组成
10 10 12
Dir Page Offset线性地址线性地址转换为物理地址的过程
1024
表项页目录 页表
Dir
Page
页架
Offset
页目录项和页表项的结构是类似的,均为 32位,其中 20位是页表位置 /页架号,其余的未包括了由硬件设置的供操作系统使用的引用位、修改位、保护位、和其他一些有用的位。
页目录项和页表项的结构
lD位:修改位,当该页程序修改后,应先纪录为淘汰做准备 。
lA位,( 读 /写 ) 引用位,凡对程序读写均置为已访问 ( accessed),淘汰时先找 A=0的页面
( Windows用 LRU法 ) 。
lP位:存在位,页面在内存时,P=1。
lU/S位:用户 /管理员 ( User/Supervisor) 位,
若 U/S=0,该页为一个管理员页面 ( OS页面 ),
用户不能访问 。
lR/W位:读 /写 ( Read/Write) 位,R/W=1时允许页修改; R/W=0时,不允许页修改;通常程序区的 R/W=0。
4.6.6 Pentium的保护机制
l0级为操作系统内核级 。 处理 I/O,存储管理,
和其他关键操作 。 该级可使用特权指令,可访问所有段和所有页 。
l1级为系统调用处理程序级 。 用户程序可以通过调用这里的过程执行系统调用,但是只有一些特定的和受保护的过程可以被调用 。
l2级为共享库过程级 。 它可以被很多正在运行的程序共享,用户程序可以调用这些过程,都去它们的数据,但是不能修改它们 。
l3级为用户程序级 。 它受到的保护最少 。
调用不同级别的过程是允许的,但是要通过一种被严格控制着的方法 。 为了执行越级调用,CALL指令必须包含一个选择符而不是地址,选择符指向一个称为调用门
( call gate) 的描述符,由它给出被调用过程的地址 。 因此要跳转到任何一个不同级别的代码段的中间都是不可能的,必须正式地指定入口点 。
陷入和中断采用了一种和调用门类似的机构,它们引用的也是描述符而不是地址,
这些描述符指向将被执行的特定过程 。 描述符的类型域可以区分是代码段,数据段,
还是各种门 。
4.7 实例研究,Linux存储管理
4.7.1 Linux的分页管理机制在 Linux中,每一个用户进程都可以访问 4GB的线性地址空间 。 其中从 0从 3GB的虚拟内存地址是用户空间,用户进程都可以直接对其进行访问 。 从 3GB到 4 GB的虚拟内存地址是内核态空间,存放仅供内核态访问的代码和数据,用户进程处于用户态时不能访问 。 当中断或系统调用发生,用户进程进行模式切换
( 处理器特权级别从 3转为 0) 。
所有进程从 3GB到 4 GB的虚拟内存地址都是一样的,又相同的页目录项和页表,
对应到同样的物理内存段 。 Linux以此方式让内核态进程共享代码段和数据段 。
另外,虚拟内存地址从 3GB 到 3
GB+4MB的一段和 0到 4MB的一段是对应的 。
Linux请求页式技术管理虚拟内存
页表分为三层:
页目录 PGD,中间页目录 PMD和页表 PTE。
在 Pentium计算机上它被简化成两层,
PGD和 PMD合二为一 。
每一个进程都有一个页目录,存储该进程所使用的内存页面情况 。 当使用 fork()创建一个进程时,分配内存页面的情况如下:
l 进程控制块 1页;
l 内核态堆栈 1页;
l 页目录 1页;
l 页表若干页 。
而使用 exec()系统调用时,分配内存页面的情况如下:
l 可执行文件的第 1页;
l 用户堆栈的 1页或几页 。
4.7.2 内存的共享和保护
Linux中内存共享是以页共享的方式实现的,共享该页的各个进程的页表项直接指向共享页,这种共享不需建立共享页表,节省内存空间,胆小率较低 。 当共享页状态发生变化时,共享该页的各进程页表均需修改,要多次访问页表 。
Linux定义了虚存段 VMA。 一个 VMA段对应一个进程的一段连续的虚存空间,它的每个页单元拥有相同的特征,如属于同一进程,相同的访问权限,同时被锁定,同时受保护 。
Linux可以对虚存段中的任一部分加锁或保护 。 与加锁有关的操作共有 4种:对指定的一段虚拟空间加锁或解锁 ( mlock和
munlock) ;对进程所有的虚拟空间加锁或解锁 ( mlockall和 munlockall) 。 虚存加锁后,它对应的物理页面驻留内存,不再被页面置换程序换出 。 而对进程的虚拟地址空间实施保护操作,就是重新设置 VMA
段的访问权限,保护操作由系统调用
mprotect实施 。
对虚存段的加锁和保护操作可以有四种方式:对整个虚存段,或虚存段的前部,中部和后部分别进行加锁和保护 。
4.7.3 物理地址空间管理
物理内存以页帧 ( page frame) 为单位,
长度固定,在一 Pentium上为 4KB。 Linux
对物理内存的管理通过 mem_map表描述,
它在系统初始化时创建,其中每一个表项的结构如下:
typedefstruct page {
struct page *next,*prev;
/* 如果该页帧的内容是文件,则 inode和 offset指出文件的 inode和偏移位置 */
struct inode *inode;
unsigned long offset;
struct page *next_hash; /*页 cache和 hash表后向指针 */
atomic_t count; /*访问该页帧的进程计数 */
unsigned flags;
unsigned dirty; /*修改标记 */
unsigned age; /*页帧年龄,越小越先换出 */
struct wait_queue *wait;
struct page *prev_hash; /*页 cache和 hash表前向指针 */
struct buffer_head *buffers; /* 如果该页帧作为缓冲区,则指示地址 */
unsigned long swap_unlock_entry;
unsigned long map_nr; /*页帧在 mem_map表中的下标 */
} mem_map_t;
在物理内存低端,紧跟 mem_map表的 bitmap表以
0位示图方式记录了所有物理内存空间的空闲状况 。 与一般位示图不同,bitmap 表分割成
NR_MEM_LISTS( 缺省为 6) 的组 。
首先是第 0 组,初始化 长度为 ( end_mem –
start_mem) / PAGE_SIZE / 20+3,每位表示一个页帧的占用情况 。
第 1组初始化长度为 ( end_mem – start_mem) /
PAGE_SIZE / 21+3,每位表示连续 2个页帧的占用情况 。 第 i 组 初 始 化 长 度 为 ( end_mem –
start_mem) / PAGE_SIZE / 2i+3,每位表示连续
2i-1个页帧的占用情况 。
Linux还把所有的空闲物理页帧组织成
NR_MEM_LISTS的双向链表,存储在
free_area数组中 。 每个链表节点的包括三个数据向,next和 prev是链表指针,
map指向 bitmap表 。
Linux采用 buddy算法分配空闲块 。 当请求分配长度为 2i个页帧的块时,首先从
free_area数组的第 i条链表开始受搜索,
如找不到,在搜索第 i+1条链表,以此类推 。 如果找到的空闲块正好等于需求,
则直接把它从来链表中删除,返回首地址 。 如果找到的空闲块大于需求,则需要把它一分为二,前半部分插入前一条链表,取后半部分 。 如果还大,则继续对分,取一半留一半,直至相等 。 同时,
bitmap表页必须相应调整 。
回收空闲块时,应该根据 bitmap表中的对应组,判断回收块的前后是否为空闲块 。 若是则合并,并调整 bitmap表对应位,从 free_area的相应空闲链表中取下该空闲块并归还 。 这是一个递归过程,
直到找不到空闲块邻居,将最大的空闲块插入 free_area的相应空闲链表 。
4.7.4 交换空间
Linux采用两种方式保存换出地页面 。 一是使用整个块设备,如硬盘的一个分区,
称作交换设备
另一种是使用文件系统的一个固定长度的文件,称作交换文件 。 两者统称为交换空间 。
交换设备和交换文件的内部格式
前 4096 个 字 节 是 一 个 以 字 符 串
,SWAP_SPACE”结尾的位图 。 位图的每一位对应于一个交换空间地页面,置位表示对应的页面可以用于换页操作 。 第 4096字节之后是真正存放换出页面的空间 。 这样每个交换空间最多可以容纳 ( 4096-10) *8-1=32687个页面 。 如果一个交换空间不够用,Linux最多允许管理 MAX_SWAPFILES( 缺省值为 8) 个交换空间 。
交换设备远比交换文件更加有效 。 在交换设备中,属于同一页面的数据总是连续存放的,第一个数据块地址一经确定,
后续的数据块可以按照顺序读出或写入 。
而在交换文件中,属于同一页面的数据虽然在逻辑上是连续的,但实际存储缺决定于拥有交换文件的文件系统 。 在大多数文件系统中,一个页面的物理存储是零散的,交换这样的页面,必须多次访问磁盘扇区,这意味着磁头的反复移动,寻道时间的增加和效率的降低 。
4.7.5 页的换进换出
4.7.5.1 页交换进程和页面换出
当物理页面不够用时,Linux存储管理系统必须释放部分物理页面,把它们的内容写到交换空间 。 内核态交换线程 kswapd完成这项功能,注意内核态线程是没有虚拟空间的线程,
它运行在内核态,使用物理地址空间 。
Kswapd不仅能够把页面换出到交换空间,也能保证系统中有足够的空闲页面以保持存储管理系统高效的运行 。
Kswapd在系统初启时由 init创建,然后调用
init_swap_timer()函数进行设定,并马上转入睡眠 。 以后每隔 10ms响应函数 swap_tick()被周期性激活,它首先察看系统中空闲页面是否变得太少,如果空闲页面足够,kswapd继续睡眠,否则环形 kswapd处理 。 Kswapd依次从三条途径缩减系统使用的物理页面:
l 缩减 page cache和 buffer cache;
l 换出 SYSTEM V共享内存占用得页面;
l 换出或丢弃进程占用得页面 。
4.7.5.2 缺页中断和页面换入
磁盘中的可执行文件映像 ( image) 一旦被映射到一个进程的虚拟空间,它就开始执行 。 当一个进程访问了一个还没有有效页表项的虚拟地址时,处理器将产生缺页中断,通知操作系统,并把缺页的虚拟地址 ( 保存在 CR2寄存器中 ) 和缺页时访问虚存的模式一并传给 Linux的缺页中断处理程序 。
系统初始化时首先设定缺页中断处理程序位
do_page_fault()。 根据控制寄存器 CR2传递的缺页地址,Linux必须找到用来表示出现缺页的虚拟存储区的 vm_area_struct结构,如果没有找到,
那么说明进程访问了一个非法存储区,系统将发出一个信号告知进程出错 。 然后系统检测缺页时访问模式是否合法,如果进程对该页的访问超越权限,系统也将发出一个信号,通知进程的存储访问出错 。 通过以上两步检查,可以确定缺页中断是否合法,进而进程进一步通过页表项中的位 P来区分缺页对应的页面是在交换空间 ( P=0且页表项非空 ) 还是在磁盘中某一执行文件映像的一部分 。 最后进行页面调入操作 。