Linux 虚拟内存

什么是虚拟内存?

先直接摘抄一段 wikipedia 上的介绍。

虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

对于 C 语言里面的变量,我们可以使用 & 运算符来获得其地址, 既然是虚拟地址,就是指这个地址是虚拟的。

虚拟地址机制不是必须的,在简单的单片机中,编写的代码编译时都需要指定物理 RAM 空间分布,不会有虚拟地址的概念,地址就是指在 RAM 中的物理地址。

虚拟内存(之所以称为虚拟内存,是和系统中的逻辑内存和物理内存相对而言的,逻辑内存是站在进程角度看到的内存,因此是程序员关心的内容。而物理内存是站在处理器角度看到的内存,由操作系统负责管理。虚拟内存可以说是映射到这两种不同视角内存的一个技术手段。)技术就是一种由操作系统接管的按需动态内存分配的方法,它允许程序不知不觉中使用大于实际物理空间大小的存储空间(其实是将程序需要的存储空间以页的形式分散存储在物理内存和磁盘上),所以说虚拟内存彻底解放了程序员,从此程序员不用过分关心程序的大小和载入,可以自由编写程序了,繁琐的事情都交给操作系统去做吧。

为什么需要虚拟内存?

我们知道程序代码和数据必须驻留在内存中才能得以运行,然而系统内存数量很有限,往往不能容纳一个完整程序的所有代码和数据,更何况在多任务系统中,可能需要同时打开子处理程序,画图程序,浏览器等很多任务,想让内存驻留所有这些程序显然不太可能。因此首先能想到的就是将程序分割成小份,只让当前系统运行它所有需要的那部分留在内存,其它部分都留在硬盘。当系统处理完当前任务片段后,再从外存中调入下一个待运行的任务片段。的确,老式系统就是这样处理大任务的,而且这个工作是由程序员自行完成。但是随着程序语言越来越高级,程序员对系统体系的依赖程度降低了,很少有程序员能非常清楚的驾驭系统体系,因此放手让程序员负责将程序片段化和按需调入轻则降低效率,重则使得机器崩溃;再一个原因是随着程序越来越丰富,程序的行为几乎无法准确预测,程序员自己都很难判断下一步需要载入哪段程序。因此很难再靠预见性来静态分配固定大小的内存,然后再机械地轮换程序片进入内存执行。系统必须采取一种能按需分配而不需要程序员干预的新技术。

X86 的虚拟内存技术

GDT/LDT

GDT 和 LDT 都是在 80286 的时候引入 x86 体系的,LDT 和 GDT 有着类似的结构。 LDT 的出现就是为了多进程使用独立地址空间来服务的,通常每个进程一个 LDT, 而共享内存和内核内存则使用 GDT。 每个程序根据段描述符来确定基址,而且每个 Entry 里面还有 limit 字段,正好可以对程序访问空间作限制。但在 80386 引入了更优秀的分页技术后,LDT 基本上就不再使用了。

分页

分页作为当前虚拟内存技术的实现,肯定有比 LDT 更好的地方,但它们的实现思路都是类似的。操作系统为每个进程维护一个 handle,这个handle关联的是该进程从虚拟地址到物理地址转换的相关数据块。在 LDT 中 handle就是 LDT 指针与长度, 数据块就是 LDT 自身。分页模式下数据块叫做 paging structure, handler 是指向其的指针。

paging structure 有 4096 bytes, 包含有独立的 entries,不同模式下每个 entry 的大小不同。 每个 entry 包含一个物理地址,可以指向一个 page frame,也可以指向另一个 paging structur,也就是级联的方式。 指向第一个 page structure 的指针在 CR3 寄存器里, 之后从线性地址到物理地址的过程就是一个迭代的过程。线性地址的一部分用来指示对应的 entry, 该 entry 如果指向的是另一个 page structure 则继续,直到指向了一个 page frame则表示地址转换完成,使用最后这个 entry 作为基址,线性地址剩余部分表示偏移。

现在专门讨论 32bit 模式下的分页。32bit下每个 entry 4bytes,每个 paging structure包含 1024 个 entries, 需要 10bit 来区分每个 entry。实际上 32bit 模式下使用了 2 级 pageing structure。 第一级称为 Page Directory, 使用 32bit 线性地址的 bits 31-22 来区分, 第二级称为 Page Table, 使用 32bit 线性地址的 bits 21-12 来区分,剩下的 bits 11-0正好是用来计算在 4K page 里的偏移。

在所有的线性地址到物理地址翻译中, CR3,PDPTE,PDT, PTE等存储的都是下一步的基址, 线性地址中存的则是偏移。

实现虚拟内存

  虚拟内存是将系统硬盘空间和系统实际内存联合在一起供进程使用,给进程提供了一个比内存大得多的虚拟空间。在程序运行时,只要把虚拟地址空间的一小部分存储到内存,其余都存储在硬盘上(也就是说程序虚拟空间就等于实际物理内存加部分硬盘空间)。当被访问的虚拟地址不在内存时,则说明该地址未被存储到内存,而是被存贮在硬盘中,因此需要的虚拟存储地址随即被调入到内存;同时当系统内存紧张时,也可以把当前不用的虚拟存储空间换出到硬盘,来腾出物理内存空间。系统如此周而复始地运转——换入、换出,而用户几乎无法查觉,这都是拜虚拟内存机制所赐。

  Linux的swap分区就是硬盘专门为虚拟存储空间预留的空间。经验大小应该是内存的两倍左右。有兴趣的话可以使用 swapon -s 查看交换分区大小。

  大道理很好理解,无非是用内存和硬盘空间合成为虚拟内存空间。但是这一过程中反复运行的地址翻译(虚拟地址翻译到物理地址)和虚拟地址换入换出却值得仔细推敲。系统到底是怎么样把虚拟地址翻译到物理地址上的呢?内存又如何能不断地和硬盘之间换入换出虚拟地址呢?

  利用段机制能否回答上述问题呢?逻辑地址通过段机制后变为一个32位的地址,足以覆盖4G的内存空间,当程序需要的虚拟地址不在内存时,只依靠段机制很难进行虚拟空间地换入换出,因为不大方便把整段大小的虚拟空间在内存和硬盘之间调来调去(老式系统中,会笨拙地换出整段内存甚至整个进程,想想这样做会有那些恶果吧!)。所以很有必要寻找一个更小更灵活的存储表示单位,这样才方便虚拟地址在硬盘和内存之间调入调出。这个更小的存储管理单位便是页(4K大小)。管理页换入换出的机制被称为页机制。(关于页机制请参考《分段与分页机制小结》)。

  我们知道线性地址(可以理解为逻辑虚拟地址)空间的大小为4GB,那怎样来划分页的大小合理呢?IA32的标准页大小为4KB,也就是说,线性地址空间被分成1M个4KB大小的页。分大点可以不?可以,但是相应的内存中页面的数目就减少,带来的结果是一个页面的空间过大,那么对于一个小数据来说,给他分配一个块是不是有点浪费?要不分小点,在内存运行时数据被分散到了不同的块,读起来也是很费劲的,因此,就巧妙的把页的大小设为4KB。

  因为使用页机制的原因,通过段机制转换得到的地址仅仅是作为一个中间地址——线性地址,该地址不代表实际物理地址,而是代表整个进程的虚拟空间地址。在线性地址的基础上,页机制接着会处理线性地址映射:当需要的线性地址(虚拟空间地址)不在内存时,便以页为单位从磁盘中调入需要的虚拟内存;当内存不够时,又会以页为单位把内存中虚拟空间的换出到磁盘上。可见,利用页来管理内存和磁盘(虚拟内存)大大方便了内存管理的工作。毫无疑问,页机制和虚拟内存管理简直是“绝配”。

时间: 2024-11-04 21:23:40

Linux 虚拟内存的相关文章

对Linux 虚拟内存和物理内存的理解以及Linux下怎样增加虚拟内存

首先,让我们看下linux虚拟内存: 第一层理解 1.         每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构 2.       一个新进程建立的时候,将会建立起自己的内存空间,此进程的数据,代码等从磁盘拷贝到自己的进程空间,哪些数据在哪里,都由进程控制表中的task_struct记录,task_struct中记录中一条链表,记录中内存空间的分配情况,哪些地址有数据,哪些地址无数据,哪些可读,哪些可写,都可以通过这个链表记录 3.       每个进程已经分配的内存

Linux虚拟内存管理(glibc)

转:https://blog.csdn.net/tengxy_cloud/article/details/53067396 https://www.cnblogs.com/purpleraintear/p/6051562.html 在使用mysql作为DB开发的兑换券系统中,随着分区表的不断创建,发现mysqld出现了疑似"内存泄露"现象,但通过 valgrind 等工具检测后,并没发现类似的问题(最终原因是由于glibc的内存碎片造成). 最近在做 MySQL 版本升级时( 5.1-

Linux虚拟内存相关知识

Linux 的虚拟内存管理有几个关键概念: 1.每个进程都有独立的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址: 2.虚拟地址可通过每个进程上的页表(在每个进程的内核虚拟地址空间)与物理地址进行映射,获得真正物理地址: 3.如果虚拟地址对应物理地址不在物理内存中,则产生缺页中断,真正分配物理地址,同时更新进程的页表:如果此时物理内存已耗尽,则根据内存替换算法淘汰部分页面至物理磁盘中. Linux 虚拟地址空间如何分布? Linux 使用虚拟地址空间,大大增加了进程的寻址空间, 虚拟地址空

Linux虚拟内存的作用

要深入了解linux内存运行机制,需要知道下面提到的几个方面:首先,Linux系统会不时的进行页面交换操作,以保持尽可能多的空闲物理内存,即使并没有什么事情需要内存,Linux也会交换出暂时不用的内存页面.这可以避免等待交换所需的时间. 其次,linux进行页面交换是有条件的,不是所有页面在不用时都交换到虚拟内存,linux内核根据”最近最经常使用“算法,仅仅将一些不经常使用的页面文件交换到虚拟内存,有时我们会看到这么一个现象:linux物理内存还有很多,但是交换空间也使用了很多.其实,这并不奇

Linux——虚拟内存

问题的提出 pro1.c #include <stdio.h> #include <stdlib.h> main() { int *a=malloc(4); *a=9999; //*(a+1)=1000; //*(a+1000)=10000; printf("%p\n",a); while(1); } gcc pro1.c -omain1    运行 main1   结果:0x8a01008 pro2.c #include <stdio.h> #in

linux虚拟内存子系统简介

MMU(内存管理单元) 负责将虚拟地址转换为物理地址,数据存放在主存上,cpu访存时至少需要两次,第一次获取物理地址:第二次才获取数据. TLB:为了改善虚拟地址到物理地址的转换速度,提高cpu访存速度. 原理:TLB利用的是页表的访问局部性,即当一个转换的虚拟页号被使用时,它可能在不久 的将来再次被使用到.TLB是一种高速缓存,当cpu访问第一次某个线性地址时,通 过计算获得对应的物理地址,同时,该线性地址和物理地址的对应关系保存在一个 TLB表项中,以后对同一线性地址的访问,直接从TLB表项

Linux虚拟内存和物理地址的理解【转】

本文转载自:http://blog.csdn.net/dlutbrucezhang/article/details/9058583 在多任务操作系统中的每一个进程都运行在一个属于它自己的内存沙盘中.这个沙盘就是虚拟地址空间(virtual address space),在32位模式下它总是一个4GB的内存地址块.这些虚拟地址通过页表(page table)映射到物理内存,页表由操作系统维护并被处理器引用.每一个进程拥有一套属于它自己的页表,但是还有一个隐情.只要虚拟地址被使能,那么它就会作用于这

Linux虚拟内存设置

1.首先,用拥有ROOT权限的用户登入到系统,进行创建swap分区 dd if=/dev/zero of=/swap/swap bs=1024 count=1024000 if //输入of //输出bs //块儿大小count //总大小 2.创建Linux交换文件mkswap /swap/swap 3.立即激活/swap/swap交换文件swapon /swap/swap 4.设置成永久生效 虽然现在已经生效,但是等下次服务器重启之后.该swap虚拟磁盘会失效,为保证永久生效,还需往/etc

1g云主机升级centos8不满足centos 8 至少2g内存要求,linux虚拟内存来凑

centos8 官方说,至少2g内存,推荐4g内存,像我的个人博客,zhoulujun.cn ,这种个人博客有不赚钱,丢个5美金一个月的1g内存,1核cpu,就够了. 强制升级到centos8,nginx 和php还好,其它程序,像mysql,经常崩溃.只有增加虚拟内存来凑数了 1g内存,虚拟内存的话,也就是物理内存的2倍左右,也就是设置2g的虚拟内存,一起3g内存. free -m  查看内存,根据情况设置内存, dd if=/dev/zero of=/opt/swap bs=1024 cou