Linux虚拟内存

来源:互联网 发布:stc89c52单片机结构 编辑:程序博客网 时间:2024/05/21 11:03

前言

在过去,传统的计算机在运行程序时,都是一次性将程序所需的所有数据调入内存(物理内存)中。这样的行为总是会耗尽内存的,从而导致其它程序在一定时间内无法调入内存(物理内存)。于是在1968年,Denning.P就曾提出,“程序执行时呈现除局部性特征,即在一个较短的时间内,程序的执行仅局限于某个部分,而它所访问的存储空间也局限在某个区域中”。简单点讲就是程序在运行时,在较短时间内,并不需要全部的数据和资源,因此把程序一次性都装进内存(物理内存)中,显得很浪费。

而虚拟内存技术解决了上面的问题,它是计算机系统内存管理的一种技术。它是的程序认为它拥有连续的可用的内存(一块连续完整的地址空间),而实际上,它通常被分割为多个物理内存碎片,其余部分则存储在外部磁盘,在需要时再调入内存中。

对虚拟内存的理解

第一层理解

  1. 每个进程都有自己独立的4G内存空间,各个进程内存空间具有类似的结构。
  2. 一个进程新建立时,将会建立起自己的内存空间,次进程的数据,代码等从磁盘拷贝到自己的进程空间,哪些数据在哪里,都由进程控制表中的task_struct记录,task_struct中记录着一条链表,记录着内存空间的分配情况,哪些地址有数据,哪些地址无数据,哪些可读,哪些可写,都可以通过这个链表记录。
  3. 每个进程已经分配的内存空间,都与对应的磁盘空间映射。

这里写图片描述

问题

计算机明明没有那么多内存(n个进程的话就需要n*4G)内存?
建立一个进程,就要把磁盘上的程序文件拷贝到进程对应的内存中去,对于一个程序对应的多个进程这种情况,浪费内存!

第二层理解

  1. 每个进程的4G内存空间只是虚拟内存空间,每次访问内存空间的某个地址,都需要把地址翻译为实际物理内存地址。
  2. 所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。
  3. 进程要知道哪些内存地址上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,需要用页表来记录。
  4. 页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)。
  5. 当进程访问某个虚拟地址,去看页表,如果发现对应的数据不在物理内存中,则缺页异常。
  6. 缺页异常的处理过程,就是把进程需要的数据从磁盘上拷贝到物理内存中,如果内存已经满了,没有空地方了,那就找一个页覆盖,当然如果被覆盖的页曾经被修改过,需要将此页写回磁盘。

优点

  1. 既然每个进程的内存空间都是一致而且固定的,所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际的内存地址,这是有独立内存空间的好处。
  2. 当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存。
  3. 在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片。

另外,事实上,在每个进程创建加载时,内核只是为进程“创建”了虚拟内存的布局,具体就是初始化进程控制表中内存相关的链表,实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射),等到运行到对应的程序时,才会通过缺页异常,来拷贝数据。还有进程运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。

虚拟存储器涉及三个概念: 虚拟存储空间,磁盘空间,内存空间
这里写图片描述

可以认为虚拟空间都被映射到了磁盘空间中,(事实上也是按需要映射到磁盘空间上,通过mmap),并且由页表记录映射位置,当访问到某个地址的时候,通过页表中的有效位,可以得知此数据是否在内存中,如果不是,则通过缺页异常,将磁盘对应的数据拷贝到内存中,如果没有空闲内存,则选择牺牲页面,替换其他页面。

0 0