返回介绍

3. 虚拟内存

发布于 2024-10-12 21:58:07 字数 3302 浏览 0 评论 0 收藏 0

直接使用物理内存的问题:

  • 地址空间不隔离。
  • 缺乏有效管理机制。
  • 运行地址不确定。

为了高效管理存储器,现代系统对主存进行了抽象,称做虚拟内存(或虚拟存储器)。由操作系统和硬件共同实现,包括硬件异常、地址翻译、磁盘文件和内核程序等。

  • 将内存对应到磁盘存储器,主存只保留活动区域。(swap, pagefile.sys)
  • 为每个进程提供一致的地址空间,简化内存管理。(link, ld)
  • 无法访问物理地址,隔离进程数据确保不被破坏。
  • 提供超过物理内存大小限制的内存空间。
  • 相邻虚拟地址对应不连续的物理内存。
  • 申请内存只返回虚拟地址,无需立即分配物理内存。(首次访问触发缺页异常)
  • 用缺页异常按需分配,按需从磁盘加载程序数据。
 +-----+              +-----+              +-----------------+
 | CPU | ---> va ---> | MMU | ---> pa ---> | Physical Memory |
 +-----+              +-----+              +-----------------+

                      Memory Management Unit, Hardware.

管理单元

磁盘随机读写性能差,为获得较高性能和利用率,通过将虚拟内存分割成虚拟页(virtual page)来解决问题。

  • 分段(segmentation):将程序内容按是否需要装载到内存分段,确定起始地址和大小,并赋予读写、执行权限。解决了隔离和地址确定问题。
  • 分页(paging):将地址空间分成固定大小的页(大小由硬件或操作系统决定)。分配虚拟地址后,按页按需载入数据,或换入换出。解决内存管理问题。
$ cat /proc/19/smaps | grep -i "pagesize"

KernelPageSize:        4 kB
MMUPageSize:           4 kB

虽然虚拟地址空间非常大(AMD64/48 bit/256 TB),但实际有效存储空间是 交换空间 + 物理内存

虚拟寻址就是将虚拟地址(VA)翻译成物理地址(PA),然后再执行读写指令。每个进程都有有自己的页表,用于记录页状态。页表项(page table entry, PTE)内容包括:

  • 有效位(P):是否已在物理内存。
  • 物理地址(base addr):虚拟页对应物理页的地址。
  • 读写权限(RW):是否可读可写。
  • 超级权限(U/S):只允许内核访问,还是允许用户访问。
  • 修改位(D):加载到物理内存后,内容是否被修改。

地址翻译由集成在 CPU 内的内存管理单元(memory management unit, MMU)完成。接收一个虚拟地址,输出一个物理地址。如果目标页不在物理内存,将触发缺页异常(page fault)。

缺页异常由硬件中断触发,被操作系统捕获。特定的内核处理程序完成物理页(physical page)映射或数据换入操作。如果没有空闲物理页,则牺牲某些不活跃的页,将其数据交换(swap out)到磁盘。下次需要时,再换入。

MMU 用自带 TLB(translation lookaside buffer)缓存提高 PTE 读取效率。考虑 PTE 数量非常多,为节约内存,采取了多级页表数据结构。局部性原则让程序在任意时刻,只在一个较小的活动集上工作,被称做工作集(working set)或常驻集(resident set)。

也就是一次性激活多个页面,避免频繁换入换出,导致性能问题。 VSZ : 分配的虚拟内存大小; RSS : 物理内存常驻集大小(不包括换出部分,包括共享库、堆、栈)。

 +------+         +-----------+         +----------+         +-------------+
 | core | ------> | mmu / tlb | ------> | lx cache | ------> | memory / pt |
 +------+         +-----------+         +----------+         +-------------+
      

示例:

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[])
{
    const int n = 100 << 20;
    char *p = malloc(n * sizeof(char));

    for (int i = 0; i < n; i++)
    {
        *(p + i) = 1;

        if (i % (1<<20) == 0)
        {
            printf("%d\n", i);
            getchar();
        }
    }

    return 0;
}

分屏用 pidstat 查看 minflt/s、VSZ、RSS,观察缺页异常和物理内存按需分配。

$ pidstat -p 17 -r 1   # 每隔 5 秒输出一次缺页异常统计。
$ cat /proc/17/status

VmPeak:	    4760 kB    ; 所使用虚拟内存峰值
VmSize:	    4760 kB    ; 当前使用的虚拟内存
VmLck:	       0 kB    ; 已经锁住物理内存(不能交换到硬盘)
VmHWM:	    3864 kB    ; 所使用物理内存峰值
VmRSS:	     876 kB    ; 当前使用的物理内存

VmData:	     732 kB    ; 数据段
VmStk:	     132 kB    ; 栈
VmExe:	     484 kB    ; 代码段
VmLib:	    2016 kB    ; 加载的动态库(可能与其他进程共享)
VmPTE:	      48 kB    ; 页表
VmSwap:	     112 kB    ; 所使用交换区大小

voluntary_ctxt_switches:	3543  ; 主动上下文切换次数(IO)
nonvoluntary_ctxt_switches:	64  ; 强制上下文切换次数(time slice)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文