你们好,又碰面了,我是大家的同事全栈君。倘若您正在找激活码,请点击查看最新教程,关注关注公众号“全栈程序员社区”获取激活教程,可能之前旧版本教程早已失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
Linux虚拟储存管理剖析
摘要:本文通过解剖Linux操作系统的虚拟储存管理机制,说明了Linux虚拟储存的特性、虚拟储存器的实现方式,并基于LinuxKernelSource1.0,详尽剖析有关虚拟存诸管理的主要数据结构之间的关系。
关键字:Linux操作系统显存管理虚拟储存
Linux操作系统是一种多用户多任务、支持多种平台的开源的类Unix操作系统,其支持多种平台,在服务端可与其它商用类Unix系统抗衡,在顾客端则向Windows系列发出了强有力的挑战。自Linux诞生以来,发展迅猛,早已遭到了全球开源社区和许多商业科技大鳄的大力支持以及政府、教育机构、科研单位的注重。本文介绍了Linux虚拟储存技术的特性,并结合操作系统原理和Linux内核源码,通过剖析虚拟储存管理所需的主要数据结构及其互相关系,来更深入地了解Linux虚拟储存管理机制。本文所引述的Linux内核源代码版本为1.0。
Linux虚拟储存概述
虚拟储存器
在实储存器的管理模式中,要求作业在运行前全部倒入显存,然后就始终留驻在显存中直至运行结束,其中个别程序并没有始终处于运行状态,却常年占用着显存资源linux系统如何支持虚存,因而增加了显存的借助率,因此ubuntu linux,引入了虚拟储存器。
虚拟储存器并不以化学的方法存在,而是从逻辑上对显存容量进行扩展,提供了一个比真实显存空间大得多的逻辑地址空间的逻辑储存器。虚拟储存技术把用户地址空间和实际的储存空间区分开,在程序运行时通过动态重定位的地址映射机制将逻辑地址转换为化学地址。
所谓的“动态重定位”是指在目标程序执行过程中,在CPU访问显存之前,由硬件地址映射机构来完成即将访问的指令或数据的逻辑地址向显存的化学地址的转换。因为这些地址转换是在程序执行期间随着每条指令的数据访问手动连续地进行,所以称为“动态重定向”。
在386保护模式下,Linux可以提供的逻辑地址空间为4GB。Linux的全局描述符表定义了怎样分配逻辑地址空间:0GB–3GB分配为用户空间,用户进程可以直接访问;3GB–4GB分配为内核空间,用户进程不能访问。
段页式储存管理
Linux中的显存管理技术采用的是段页式虚存技术。它将一个进程中的程序、数据、堆栈分成若干“段”来处理,每段有一个8字节的段描述符,强调该段的起始地址、长度和存取权限等信息,这种段描述符的集合构成段描述符表,通过一个寄存器强调该表的起始位置。为了易于段长的动态变化,每段分为若干页,将须要的内容以页面为单位调入显存的化学块中,暂不执行的页面仍留在外存,以保证比实际显存容量需求更大的进程才能正常使用显存。
Linux的分段机制
Linux的分段机制就是将线性地址空间分段,借助这种段来储存代码和数据,通过对段的保护来提供一种对数据或代码的保护。按照每位段的作用和储存内容的不同,分为三类进程段:代码段、数据段和堆栈段;两类系统段:TSS段(任务状态段)和LDT段(局部描述符表段)。
在保护模式下,逻辑地址空间可达4GB。从逻辑地址到线性地址的转换由分段机制管理。段寄存器CS、DS、ES、SS、FS或GS标示一个段。这种段寄存器作为段选择器,拿来选择该段的描述符。
进程使用的是48位的逻辑地址,其中高16位是段选择符,低32位是段内的偏斜量。通过段选择符在GDT(全局描述符表)或LDT(局部描述符表)中索引相应的段描述符,以得到该段的基地址,再加上偏斜量得到逻辑地址对应的线性地址。之后通过分页地址的转换,将线性地址转换为化学地址,最后通过化学地址访问显存。
如图1
所示,为分段逻辑地址到线性地址的转换图。
图1分段逻辑地址映射到线性地址
Linux的分页机制
分页机制是在段机制以后进行的,它进一步将线性地址转换为化学地址。Linux通常使用4K字节大小的页(PAGE_SIZE,include/linux/page.h,通过更改PAGE_SHIFT定义的左移位数来更改页面大小),且每页的起始地址都被4K整除。为此,Linux把4GB的线性地址空间界定为1M个页面,采用了两级表结构。
两级表的第一级表称为页目录,储存在一个4K字节的页中,页目录表共有1K个表项,每位表项为4个字节,线性地址最高的10位(22-31位)拿来形成第一级表索引,由该索引得到的表项中的内容定位了二级表中的一个表的地址,即下级页表所在的显存块号。
第二级表称为页表,储存在一个4K字节页中,它包含了1K字节的表项,每位表项包含了一个页的数学地址。二级页表由线性地址的中间10位(12-21位)进行索引,定位页表表项,获得页的数学地址。页化学地址的高20位与线性地址的低12位产生最后的数学地址。
如图2
所示,为两级页表的地址转换图。
图2两级页表转换
Linux进程与段页式管理
每每启动一个新的进程,Linux都为其创建一个进程控制块(task_struct,include/linux/sched.h)。在创建过程中,每位进程(按照须要)创建并初始化新页目录,设置页目录基地址寄存器,在GDT中添加进程对应的TSS项和LDT项,创建并初始化该进程的LDT。
Linux采用“按需调页”的原则来分配显存页面,执行进程的页面总会在外存与显存之间不断交换,因而防止页表过多占用储存空间。创建一个进程时页面分配的情况大致是这样的:进程控制块(1页),显存态堆栈(1页),页目录(1页),页表(须要的n页)。在进程之后执行的执行中,再依照须要渐渐分配更多的显存页面。
Linux交换空间
交换空间是在外存中开辟一定的空间将至时储存从显存中调出的页面,其储存区域自然也是按页界定的。Linux采用了块设备和交换文件两种方式来保存换出的页面,并且这两种方式的内部结构是一致的。有时侯,为了优化系统性能,会定义不止1个交换空间,因此Linux实现了并行管理多个交换空间,这种交换空间均定义在同一个数据结构中(swap_info_struct[MAX_SWAPFILES],mm/swap.c,其中MAX_SWAPFILES定义为最大的交换空间数目)。
Linux虚存管理的数据结构模型
Linux虚存管理模型
按照虚拟显存具象模型,每位进程都可以互不干扰的使用所有虚拟地址。进程的虚拟显存空间被界定为小的虚拟显存区域来使用。每位显存区域是一段具有相同属性的虚拟地址空间。Linux用vm_area_struct(include/linux/mm.h)来描述一个虚拟显存区域。一个进程的所有显存区域组织成一个单向数组。进程用了一个指向vm_area_struct数组的表针,来描述进程虚拟显存空间的一个区域linux就该这么学,包括对该区域的起始和中止地址的描述。进程可以通过vm_operation_struct(include/linux/mm.h)对那些区域进行操作。
当加载关于进程虚拟地址空间的页面时,一系列的vm_area_struct将手动生成,每一个vm_area_struct描述进程的一部份,如执行代码、数据等。Linux支持了多数标准的虚拟显存操作,如读取、关闭、共享、缺页等。一旦vm_area_struct结构生成,就可以通过该结构中的指向vm_operation_struct的表针进行虚拟显存操作了。
如图3
所示,为虚存管理数据结构之间的关系。
图3虚拟储存管理的数据结构关系
数据结构介绍
vm_area_struct
Linux采用了虚拟储存区域的方法来管理虚拟储存空间,一个虚拟储存区域是某个进程的一段虚拟储存空间,该结构由vm_are_struct定义。
structvm_area_struct{
structtask_struct*vm_task;/*VMareaparameters*/
/*虚存区的起始地址*/
unsignedlongvm_start;
/*虚存区的中止地址*/
unsignedlongvm_end;
/*进程对应于虚存区的访问权限*/
unsignedshortvm_page_prot;
/*linkedlist,在数组手指向上一个虚拟显存区域*/
structvm_area_struct*vm_next;
/*linkedlist,在数组中指向共享区域*/
structvm_area_struct*vm_share;
/*指向虚存区所在文件的inode结构,若不涉及文件,则为NULL*/
structinode*vm_inode;
/*虚存区相对于文件或设备在inode结构中的偏斜量*/
unsignedlongvm_offset;
/*指向vm_operation_struct结构*/
structvm_operations_struct*vm_ops;
};
vm_operation_struct
Linux对于虚存区的操作定义在vm_operation_struct数据结构中,通过在vm_area_struct结构中使用表针vm_ops来确定该虚存区可以进行的一系列操作。
structvm_operations_struct{
/*打开操作,当内核生成一个虚存区后或则当虚存区被复制后,就用该命令打开。*/
void(*open)(structvm_area_struct*area);
/*关掉操作,当内核销毁一个虚存区时,就调用该命令。*/
void(*close)(structvm_area_struct*area);
/*处理缺页异常,当进程访问一个不属于显存的有效页面时,都会调用该命令,
/*返回该页的数学地址。*/
void(*nopage)(interror_code,
structvm_area_struct*area,unsignedlongaddress);
/*处理写保护异常,当往一个被保护的页面上写入数据时,都会调用该命令。*/
void(*wppage)(structvm_area_struct*area,unsignedlongaddress);
int(*share)(structvm_area_struct*from,structvm_area_struct*to,unsignedlongaddress);
/*取消映射操作,当内核取消虚存区的部份或则全部映射时,调用该命令;
/*当取消全部映射后,内核都会手动调用close()进行关掉操作。*/
int(*unmap)(structvm_area_struct*area,unsignedlong,size_t);
};
总结
Linux是一个功能强悍的实际的操作系统,相对于操作系统原理,每位技术环节都有其自身的特性。限于篇幅,本文仅简单介绍Linux在虚拟储存管理上的技术特征与实现的数据结构之间的关系,作为深入剖析Linux虚拟储存管理源码细节的入门性文章linux系统如何支持虚存,希望能起到抛砖引玉的作用。
参考资料
((1)AndrewS.Tanenbaum
,ModernOperatingSystems(SecondEdition),机械工业出版社,2002
(2)AbhishekNayani、MelGorman、RodrigoS.deCastro,MemoryManagementinLinux,2002
(3)刘胤杰、岳浩等,Linux操作系统教程,机械工业出版社,2004