什么是虚存?为什么需要它?
我们知道程序代码和数据必须驻留在内存中才能得以运行,然而系统内存数量很有限,往往不能容纳一个完整程序的所有代码和数据,更何况在多任务系统中,可能需要同时打开子处理程序,画图程序,浏览器等很多任务,想让内存驻留所有这些程序显然不太可能。因此首先能想到的就是将程序分割成小份,只让当前系统运行它所有需要的那部分留在内存,其它部分都留在硬盘。当系统处理完当前任务片段后,再从外存中调入下一个待运行的任务片段。的确,老式系统就是这样处理大任务的,而且这个工作是由程序员自行完成。但是随着程序语言越来越高级,程序员对系统体系的依赖程度降低了,很少有程序员能非常清楚的驾驭系统体系,因此放手让程序员负责将程序片段化和按需调入轻则降低效率,重则使得机器崩溃;再一个原因是随着程序越来越丰富,程序的行为几乎无法准确预测,程序员自己都很难判断下一步需要载入哪段程序。因此很难再靠预见性来静态分配固定大小的内存,然后再机械地轮换程序片进入内存执行。系统必须采取一种能按需分配而不需要程序员干预的新技术。
虚拟内存(之所以称为虚拟内存,是和系统中的逻辑内存和物理内存相对而言的,逻辑内存是站在进程角度看到的内存,因此是程序员关心的内容。而物理内存是站在处理器角度看到的内存,由操作系统负责管理。虚拟内存可以说是映射到这两种不同视角内存的一个技术手段。)技术就是一种由操作系统接管的按需动态内存分配的方法,它允许程序不知不觉中使用大于实际物理空间大小的存储空间(其实是将程序需要的存储空间以页的形式分散存储在物理内存和磁盘上),所以说虚拟内存彻底解放了程序员,从此程序员不用过分关心程序的大小和载入,可以自由编写程序了,繁琐的事情都交给操作系统去做吧。
实现虚拟内存
虚拟内存是将系统硬盘空间和系统实际内存联合在一起供进程使用,给进程提供了一个比内存大得多的虚拟空间。在程序运行时,只要把虚拟地址空间的一小部分映射到内存,其余都存储在硬盘上(也就是说程序虚拟空间就等于实际物理内存加部分硬盘空间)。当被访问的虚拟地址不在内存时,则说明该地址未被映射到内存,而是被存贮在硬盘中,因此需要的虚拟存储地址随即被调入到内存;同时当系统内存紧张时,也可以把当前不用的虚拟存储空间换出到硬盘,来腾出物理内存空间。系统如此周而复始地运转——换入、换出,而用户几乎无法查觉,这都是拜虚拟内存机制所赐。
Linux的swap分区就是硬盘专门为虚拟存储空间预留的空间。经验大小应该是内存的两倍左右。有兴趣的话可以使用 swapon -s 查看交换分区大小。
大道理很好理解,无非是用内存和硬盘空间合成为虚拟内存空间。但是这一过程中反复运行的地址映射(虚拟地址映射到物理地址)和虚拟地址换入换出却值得仔细推敲。系统到底是怎么样把虚拟地址映射到物理地址上的呢?内存又如何能不断地和硬盘之间换入换出虚拟地址呢?
利用段机制能否回答上述问题呢?逻辑地址通过段机制后变为一个32位的地址,足以覆盖4G的内存空间,当程序需要的虚拟地址不在内存时,只依靠段机制很难进行虚拟空间地换入换出,因为不大方便把整段大小的虚拟空间在内存和硬盘之间调来调去(老式系统中,会笨拙地换出整段内存甚至整个进程,想想这样做会有那些恶果吧!)。所以很有必要寻找一个更小更灵活的存储表示单位,这样才方便虚拟地址在硬盘和内存之间调入调出。这个更小的存储管理单位便是页(4K大小)。管理页换入换出的机制被称为页机制。
因为使用页机制的原因,通过段机制转换得到的地址仅仅是作为一个中间地址——线性地址,该地址不代表实际物理地址,而是代表整个进程的虚拟空间地址。在线性地址的基础上,页机制接着会处理线性地址映射:当需要的线性地址(虚拟空间地址)不在内存时,便以页为单位从磁盘中调入需要的虚拟内存;当内存不够时,又会以页为单位把内存中虚拟空间的换出到磁盘上。可见,利用页来管理内存和磁盘(虚拟内存)大大方便了内存管理的工作。毫无疑问,页机制和虚拟内存管理简直是“绝配”。
使用页机制,4G空间被分成2的20次方个4K大小的页面(页面也可定为4M大小),因此定位页面需要的索引表(页表)中每个索引项至少需要20位,但是在页表项中往往还需要附加一些页属性,所以页表项实际为32位,其中12位用来存放诸如“页是否存在于内存”或“页的权限”等信息。
前面我们提到了线性地址是32位。它其中高20位是对页表的索引,低12位则给出了页面中的偏移。线性地址经过页表找到页面基地址后和低12位偏移量相加就形成了最终需要的物理地址了。
在实际使用中,并非所有页表项都是被存放在一个大页表里,因为每个页表项占4个字节,如果要在一个表中存放2的20次方个页表项,就需要4M的连续存储空间。这么大的连续空间可不好找,因此往往会把页表分级存储,比如分两级,那么每级页表只需要4k连续空间了。
两级页表搜索如同看章回小说,先找到在哪一章里,然后在找在该章下的哪一节。具体过程看看下图:
综上所述,地址转换工作需要两种技术,一是段机制,二是页机制。段机制处理逻辑地址向线性地址的映射;页机制则负责把线性地址映射为物理地址。两级映射共同完成了从程序员看到的逻辑地址转换到处理器看到的物理地址这一艰巨任务。
你可以将这两种机制分别比作一个地址转换函数,段机制的变量是逻辑地址,函数值是线性地址;页机制的变量是线性地址,函数值是物理地址。地址转换过程如下所示。
逻辑地址——(段函数)——>线性地址——(页函数)——>物理地址。
虽然段机制和页机制都参与映射,但它们分工不同,而且相互独立互不干扰,彼此之间不必知道对方是否存在。
下面我们结合Linux实例简要地看看段页机制如何使用。
Linux中的分段策略
段机制在Linux里用得有限,并没有被完全利用。每个任务并未分别安排各自独立的数据段,代码段,而是仅仅最低限度的利用段机制来隔离用户数据和系统数据——Linux只安排了四个范围一样的段,内核数据段,内核代码段,用户数据段,用户代码段,它们都覆盖0-4G的空间,所不同的是各段属性不同,内核段特权级为0,用户段特权级为3。这样分段,避免了逻辑地址到线性地址的转换步骤(逻辑地址就等于线性地址),但仍然保留了段的等级这层最基本保护。
每个用户进程都可以看到4G大小的线性空间,其中0-3G是用户空间,用户态进程可以直接访问;从3G-4G空间为内核空间,存放内核代码和数据,只有内核态进程能够直接访问,用户态进程不能直接访问,只能通过系统调用和中断进入内核空间,而这时就要进行的特权切换。
说到特权切换,就离不开任务门,陷阱门/中断门等概念。陷阱门和中断门是在发生陷阱和中断时,进入内核空间的通道。调用门是用户空间程序相互访问时所需要的通道,任务门比较特殊,它不含任何地址,而是服务于任务切换(但linux任务切换时并未真正采用它,它太麻烦了)。
对于各种门系统都会有对应的门描述符,和段描述符结构类似,门描述符也是由对应的门选择字索引,并且最终会产生一个指向特定段内偏移地址的指针。这个指针指向的就是将要进入的入口。利用门的目的就是保证入口可控,不至于进入到内核中不该访问的位置。
Linux中的分页策略
看看linux中如何使用分页。
Linux中每个进程都会有各自不同的页表,也就是说进程的映射函数互不相同,保证每个进程虚拟地址不会映射到相同的物理地址上。这是因为进程之间必须相互独立,各自的数据必须隔离,防止信息泄漏。
需要注意的是,内核作为必须保护的单独部分,它有自己独立的页表来映射内核空间(并非全部空间,仅仅是物理内存大小的空间),该页表(swapper_pg_dir)被静态分配,它只来映射内核空间(swapper_pg_dir只用到768项以后的项——768个页目录可映射3G空间)。这个独立页表保证了内核虚拟空间独立于其他用户程序空间,也就是说其他进程通常状态下和内核是没有联系的(在编译内核的时候,内核代码被指定链接到3G以上空间),因而内核数据也就自然被保护起来了。
那么在用户进程需要访问内核空间时如何做呢?
Linux采用了个巧妙的方法:用户进程页表的前768项映射进程空间(<3G,因为LDT 中只指定基地址为0,范围只能到0xc0000000),如果进程要访问内核空间,如调用系统调用,则进程的页目录中768项后的表项将指向swapper_pg_dir的768项后的项,所以一旦用户陷入内核,就开始使用内核的页表swapper_pg_dir了,也就是说可以访问内核空间了。
我们知道程序代码和数据必须驻留在内存中才能得以运行,然而系统内存数量很有限,往往不能容纳一个完整程序的所有代码和数据,更何况在多任务系统中,可能需要同时打开子处理程序,画图程序,浏览器等很多任务,想让内存驻留所有这些程序显然不太可能。因此首先能想到的就是将程序分割成小份,只让当前系统运行它所有需要的那部分留在内存,其它部分都留在硬盘。当系统处理完当前任务片段后,再从外存中调入下一个待运行的任务片段。的确,老式系统就是这样处理大任务的,而且这个工作是由程序员自行完成。但是随着程序语言越来越高级,程序员对系统体系的依赖程度降低了,很少有程序员能非常清楚的驾驭系统体系,因此放手让程序员负责将程序片段化和按需调入轻则降低效率,重则使得机器崩溃;再一个原因是随着程序越来越丰富,程序的行为几乎无法准确预测,程序员自己都很难判断下一步需要载入哪段程序。因此很难再靠预见性来静态分配固定大小的内存,然后再机械地轮换程序片进入内存执行。系统必须采取一种能按需分配而不需要程序员干预的新技术。
虚拟内存(之所以称为虚拟内存,是和系统中的逻辑内存和物理内存相对而言的,逻辑内存是站在进程角度看到的内存,因此是程序员关心的内容。而物理内存是站在处理器角度看到的内存,由操作系统负责管理。虚拟内存可以说是映射到这两种不同视角内存的一个技术手段。)技术就是一种由操作系统接管的按需动态内存分配的方法,它允许程序不知不觉中使用大于实际物理空间大小的存储空间(其实是将程序需要的存储空间以页的形式分散存储在物理内存和磁盘上),所以说虚拟内存彻底解放了程序员,从此程序员不用过分关心程序的大小和载入,可以自由编写程序了,繁琐的事情都交给操作系统去做吧。
实现虚拟内存
虚拟内存是将系统硬盘空间和系统实际内存联合在一起供进程使用,给进程提供了一个比内存大得多的虚拟空间。在程序运行时,只要把虚拟地址空间的一小部分映射到内存,其余都存储在硬盘上(也就是说程序虚拟空间就等于实际物理内存加部分硬盘空间)。当被访问的虚拟地址不在内存时,则说明该地址未被映射到内存,而是被存贮在硬盘中,因此需要的虚拟存储地址随即被调入到内存;同时当系统内存紧张时,也可以把当前不用的虚拟存储空间换出到硬盘,来腾出物理内存空间。系统如此周而复始地运转——换入、换出,而用户几乎无法查觉,这都是拜虚拟内存机制所赐。
Linux的swap分区就是硬盘专门为虚拟存储空间预留的空间。经验大小应该是内存的两倍左右。有兴趣的话可以使用 swapon -s 查看交换分区大小。
大道理很好理解,无非是用内存和硬盘空间合成为虚拟内存空间。但是这一过程中反复运行的地址映射(虚拟地址映射到物理地址)和虚拟地址换入换出却值得仔细推敲。系统到底是怎么样把虚拟地址映射到物理地址上的呢?内存又如何能不断地和硬盘之间换入换出虚拟地址呢?
利用段机制能否回答上述问题呢?逻辑地址通过段机制后变为一个32位的地址,足以覆盖4G的内存空间,当程序需要的虚拟地址不在内存时,只依靠段机制很难进行虚拟空间地换入换出,因为不大方便把整段大小的虚拟空间在内存和硬盘之间调来调去(老式系统中,会笨拙地换出整段内存甚至整个进程,想想这样做会有那些恶果吧!)。所以很有必要寻找一个更小更灵活的存储表示单位,这样才方便虚拟地址在硬盘和内存之间调入调出。这个更小的存储管理单位便是页(4K大小)。管理页换入换出的机制被称为页机制。
因为使用页机制的原因,通过段机制转换得到的地址仅仅是作为一个中间地址——线性地址,该地址不代表实际物理地址,而是代表整个进程的虚拟空间地址。在线性地址的基础上,页机制接着会处理线性地址映射:当需要的线性地址(虚拟空间地址)不在内存时,便以页为单位从磁盘中调入需要的虚拟内存;当内存不够时,又会以页为单位把内存中虚拟空间的换出到磁盘上。可见,利用页来管理内存和磁盘(虚拟内存)大大方便了内存管理的工作。毫无疑问,页机制和虚拟内存管理简直是“绝配”。
使用页机制,4G空间被分成2的20次方个4K大小的页面(页面也可定为4M大小),因此定位页面需要的索引表(页表)中每个索引项至少需要20位,但是在页表项中往往还需要附加一些页属性,所以页表项实际为32位,其中12位用来存放诸如“页是否存在于内存”或“页的权限”等信息。
前面我们提到了线性地址是32位。它其中高20位是对页表的索引,低12位则给出了页面中的偏移。线性地址经过页表找到页面基地址后和低12位偏移量相加就形成了最终需要的物理地址了。
在实际使用中,并非所有页表项都是被存放在一个大页表里,因为每个页表项占4个字节,如果要在一个表中存放2的20次方个页表项,就需要4M的连续存储空间。这么大的连续空间可不好找,因此往往会把页表分级存储,比如分两级,那么每级页表只需要4k连续空间了。
两级页表搜索如同看章回小说,先找到在哪一章里,然后在找在该章下的哪一节。具体过程看看下图:
综上所述,地址转换工作需要两种技术,一是段机制,二是页机制。段机制处理逻辑地址向线性地址的映射;页机制则负责把线性地址映射为物理地址。两级映射共同完成了从程序员看到的逻辑地址转换到处理器看到的物理地址这一艰巨任务。
你可以将这两种机制分别比作一个地址转换函数,段机制的变量是逻辑地址,函数值是线性地址;页机制的变量是线性地址,函数值是物理地址。地址转换过程如下所示。
逻辑地址——(段函数)——>线性地址——(页函数)——>物理地址。
虽然段机制和页机制都参与映射,但它们分工不同,而且相互独立互不干扰,彼此之间不必知道对方是否存在。
下面我们结合Linux实例简要地看看段页机制如何使用。
Linux中的分段策略
段机制在Linux里用得有限,并没有被完全利用。每个任务并未分别安排各自独立的数据段,代码段,而是仅仅最低限度的利用段机制来隔离用户数据和系统数据——Linux只安排了四个范围一样的段,内核数据段,内核代码段,用户数据段,用户代码段,它们都覆盖0-4G的空间,所不同的是各段属性不同,内核段特权级为0,用户段特权级为3。这样分段,避免了逻辑地址到线性地址的转换步骤(逻辑地址就等于线性地址),但仍然保留了段的等级这层最基本保护。
每个用户进程都可以看到4G大小的线性空间,其中0-3G是用户空间,用户态进程可以直接访问;从3G-4G空间为内核空间,存放内核代码和数据,只有内核态进程能够直接访问,用户态进程不能直接访问,只能通过系统调用和中断进入内核空间,而这时就要进行的特权切换。
说到特权切换,就离不开任务门,陷阱门/中断门等概念。陷阱门和中断门是在发生陷阱和中断时,进入内核空间的通道。调用门是用户空间程序相互访问时所需要的通道,任务门比较特殊,它不含任何地址,而是服务于任务切换(但linux任务切换时并未真正采用它,它太麻烦了)。
对于各种门系统都会有对应的门描述符,和段描述符结构类似,门描述符也是由对应的门选择字索引,并且最终会产生一个指向特定段内偏移地址的指针。这个指针指向的就是将要进入的入口。利用门的目的就是保证入口可控,不至于进入到内核中不该访问的位置。
Linux中的分页策略
看看linux中如何使用分页。
Linux中每个进程都会有各自不同的页表,也就是说进程的映射函数互不相同,保证每个进程虚拟地址不会映射到相同的物理地址上。这是因为进程之间必须相互独立,各自的数据必须隔离,防止信息泄漏。
需要注意的是,内核作为必须保护的单独部分,它有自己独立的页表来映射内核空间(并非全部空间,仅仅是物理内存大小的空间),该页表(swapper_pg_dir)被静态分配,它只来映射内核空间(swapper_pg_dir只用到768项以后的项——768个页目录可映射3G空间)。这个独立页表保证了内核虚拟空间独立于其他用户程序空间,也就是说其他进程通常状态下和内核是没有联系的(在编译内核的时候,内核代码被指定链接到3G以上空间),因而内核数据也就自然被保护起来了。
那么在用户进程需要访问内核空间时如何做呢?
Linux采用了个巧妙的方法:用户进程页表的前768项映射进程空间(<3G,因为LDT 中只指定基地址为0,范围只能到0xc0000000),如果进程要访问内核空间,如调用系统调用,则进程的页目录中768项后的表项将指向swapper_pg_dir的768项后的项,所以一旦用户陷入内核,就开始使用内核的页表swapper_pg_dir了,也就是说可以访问内核空间了。
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
暂无评论...
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
2024年11月24日
2024年11月24日
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]