2

为什么进程使用的内存尺寸(虚拟存储)可以比物理内存还大?

 2 years ago
source link: https://os.51cto.com/article/715800.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

为什么进程使用的内存尺寸(虚拟存储)可以比物理内存还大?-51CTO.COM

为什么进程使用的内存尺寸(虚拟存储)可以比物理内存还大?
作者:我不想种地 2022-08-08 07:33:11
虚拟存储是计算机系统的重要概念,可以说,理解好虚拟存储便是掌握了内存管理的钥匙。
d1726517916ce776ad72690371bff6214d5c76.jpg

为什么一个进程所需的存储空间大小能超过物理内存的大小?操作系统是如何管理机器上运行的多个进程的内存的?进程间共享存储是如何做到的?通过top命令查看的VIRT和RES指标有什么不同?所有这些问题都跟虚拟存储这个概念相关,虚拟存储是计算机系统的重要概念,可以说,理解好虚拟存储便是掌握了内存管理的钥匙。

图片
  • 进程是执行中的程序,一个进程就是一个执行中的程序实例,同一个程序可以有多个执行的实例,对应多个进程。
  • 系统上同时运行的多个进程共享机器的CPU和存储资源,每个进程(线程)有一个独立的逻辑执行流,它提供一种假象,好像在独占的使用处理器;同时,每个进程有一个私有地址空间,这提供另一个假象,它好像在独占的使用存储器。
  • 虚拟存储是一层抽象,它为每个进程提供了一致的、私有的地址空间,借助这一层关键抽象,计算机科学中最深刻最成功的概念“进程”才得以实施。
  • 虚拟存储简化了存储器管理,链接器以独立于内存物理地址的方式构建可执行程序。

虚拟存储空间被分为内核空间和用户空间两部分,并非所有虚拟地址空间都会分配物理内存,只有实际使用的才会分配物理内存,这也体现在通过top命令查看进程的VIRT和RES两项指标的数值差异上。

  • 物理内存可视为一个巨大的字节数组,每个元素占用一个字节,有自己的独立编号(也就是地址),这个地址叫物理地址(Physical Address, PA)。
  • 物理内存资源被运行在系统中的所有进程共享,就像CPU资源被运行在系统中的所有进程/线程共享一样。
  • 应用程序中所使用的地址叫虚拟地址(Virtual Address, VA),每个进程都拥有独立的私有虚拟地址空间,进程之间的虚拟存储是隔离的,既进程A不通过特殊手段无法访问进程B的某个虚拟地址。
  • 虚拟地址在访问真正的物理内存的时候,需要被转换为物理地址, 虚拟地址到物理地址的转换过程叫地址翻译。
  • CPU硬件和操作系统合作完成地址翻译,CPU芯片上的存储管理单元(MMU)查询内存中的页表动态完成地址翻译,而页表的内容由操作系统管理维护,这项工作由底层系统默默完成,对应用程序透明。
  • 进程X和进程Y中的同一虚拟地址会被映射到不同物理地址,多个进程也可以通过共享存储技术(Shared Memory)映射到同一块物理内存。
  • 32位系统的虚拟地址范围是[0-2^32],总共4G Byte,这个地址的集合叫地址空间,64位系统拥有更大的地址空间,但不是2^64。

虽然进程拥有如此大的虚拟地址空间,但它通常不会真正占用这么大存储空间。

系统以页为单位管理存储,32位系统上,PageSize通常为4K字节,64位系统上,PageSize通常为8K字节。

物理存储器(内存)被以PageSize为单位分割为物理页(Physical Page, PP),[0-4096)的连续空间被分为第1页,[4096,8192)的连续空间被分为第2页,以此类推,PageSize大小的物理页也被称为页帧。

虚拟存储空间也被按同样的PageSize分割成虚拟页(Virtual Page, VP),虚拟页分为已分配和未分配两种状态,而已分配的页又被细分为未缓存和已缓存两种状态。

进程的所有已分配的虚拟存储页构成进程的有效虚拟存储空间,对未分配的虚拟存储空间的访问非法,将触发异常(例如常见的段错误)

通过调用malloc/new/mmap等编程接口分配虚拟存储页,对物理内存页的分配由系统负责。

分页后,一个虚拟地址被分为2部分:页编号 + 页内偏移。

操作系统在内存中为每个进程维护一个页表(PageTable, PT), 地址翻译硬件通过查询存放在物理存储器中的页表,来找到对应的物理地址。

每个虚拟页对应一个页表条目(Page Table Entry, PTE),页表视为页表条目的数组,页号就是数组下标(索引)。

每个页表条目包含该虚拟页是否已分配,对未分配的页的访问非法,如果已分配,则又要区分是否已缓存(对应到物理内存页)和未缓存。

如果已缓存(页命中),则页表条目包含该虚拟页对应的物理地址;如果未缓存(页命失),则页表条目包含该虚拟页对应磁盘上该虚拟页的起始位置,系统在物理存储器中挑选一个牺牲页,并将虚拟页从磁盘拷贝到内存,这个过程叫换页(swapping)

牺牲页的内容需要从内存拷贝到磁盘虚拟页,这个过程叫换出(swap out),被缓存的新页需要从磁盘拷贝到内存,这个过程叫换入(swap in),因为牵扯到磁盘操作,过程中,一直等待,成本很高,这个成本被称为命失惩罚,但根据局部性原理,程序经历启动阶段的初始后,通常只会在一个较小的活动页面集上工作,这便能保证,虽然惩罚的成本看似很高,但实际上,它依然工作的很好。

通过页面调度,我们的程序能够在超过物理内存容量的虚拟存储空间下工作,且多个进程间,能有效的共享稀缺的内存资源。

理解这个过程对掌握内存管理和优化技术至关重要。

linux进程虚拟存储空间

图片

linux进程的虚拟存储空间自底向上分为:代码段、全局变量段、堆、共享存储区、栈、内核段。

程序启动时,加载器会将编译后的可执行程序文件中的.text节拷贝到代码段,.data拷贝到全局变量段,每个函数(内联除外)编译后都会占存储空间,进文本节,程序执行中,会从代码段源源不断的加载指令。

堆向上生长,brk指向堆顶,通过malloc/new等编程接口从堆分配内存,动态分配的内存块需要手动释放。

栈向下生长,局部变量位于栈中,函数调用时的参数也经栈传递,函数调用链所需内存由栈提供,栈是自动伸缩的,每个线程会有独立的栈,每个栈的空间有限(4/8M,可调节),所以不能局部变量不能过大,递归过深有爆栈分险。

堆内存和栈内存本质上都是存储区,位于进程的不同区段,只有使用方式上的不同,没有物理介质上的不同,都会经地址翻译到物理内存。

栈和堆之间是共享存储区,通过共享存储编程接口shmget创建的存储段位于该段,标准库的代码在进程间也被共享,这样能够节省内存。

内核段存储空间,用户态代码不可访问,但用户代码调用系统调用、或者触发异常,进程陷入内核,会执行内核段的代码+访问内核态数据。

注意:代码段并非从0开始,而是从特定地址开始,0x8048000(32位系统),0x400000(64位系统)

图片

段页式内存管理

早期计算机系统,采用段页式的内存管理,既有分页,又有分段,段内包含页,但linux系统简化了这个管理方式,进程的虚拟地址空间只有1段,所以相当于变相的废弃了分段。

如果以4K为一页,那么32位系统,虚拟内存空间为4G(2^32),因为页表是进程私有,所以,一个进程的虚拟地址空间包含1M页,需要1M页表项(64位更多),而系统中经常成百上千个进程,这个内存占用量太大了,所以,实际上,操作系统使用多级页表巧妙的解决了这个问题。

图片

多级页表是压缩页表内存占用的惯用法,引入多级页表后,顶级页表的一个表项不再表示4K/8K的地址范围,它大的多,只有一级表项表示的范围被分配,其对应的2级页表才会被存储,所以极大的节省了内存空间,而因为进程实际分配的虚拟页,只是整个地址空间的很小一部分,所以,我们得以以小的存储代价,支撑很大的地址范围。

每次地址翻译,都需要查询内存页表,如果页表条目不在缓存中,则开销很大,为了加快内地翻译速度,便引入了TLB(翻译后备缓存),TLB是MMU中的一个PTE的小的缓存,MMU在走地址翻译的时候,先从TLB查找PTE,失败了再去PageTable里找,还是因为局部性,TLB极大的加速了地址翻译的过程。

软硬件协作

  • 地址翻译,需要操作系统、MMU(TLB)协作完成
  • 操作系统为每个进程维护页表
  • MMU先查TLB缓存,没找到,再查页表
  • 进程调度后,页表基址寄存器会修改指向新的运行进程的页表其实地址

page fault

  • page fault:实际上并不是真正的错误,没有名字看起来这么严重,指令执行时,引发缺页(page fault)会触发异常,操作系统的异常处理程序会妥善处置这个异常,并再次发射这个指令,这时候,因为请求的地址已经被装载到内存,指令得以正常进行。
  • major page fault: 也叫hard page fault,major page fault主要是由swapping机制引入的,因为牵扯到磁盘io,主要由软件完成,所以速度较慢,可通过swapon/swapoff开关系统交换分区,也可以设置修改系统设置:swappiness
  • minor page fault:也叫soft page fault,指需要访问的代码/数据已经在物理内存页中,但页表中的映射还没有建立,需要MMU建立物理内存和虚拟地址空间的映射关系。当一个进程在调用malloc获取虚拟空间地址后,首次访问该地址会发生一次soft page fault。多个进程访问同一个共享内存中的数据,当某些进程还没有建立起映射关系,访问时也会出现soft page fault。

可以通过命令:ps -eo min_flt,maj_flt,cmd 查看系统各个进程的page fault情况。

当程序通过malloc分配1G字节的内存时,系统并不会为进程分配1G的物理内存,而只是分配1G的虚拟内存页,当之后真正访问某个页的时候,系统才会为它分配物理内存,如果系统物理内存被耗尽,则会挑选一个内存页,交换出去(把页的内容写入磁盘页),通过这样的策略,我们能获得比物理内存更大的存储空间,提高了内存资源的利用率,也使得进程之间共享存储变得可能。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK