操作系统内存管理

虚拟内存

虚拟内存的作用

  1. 每个进程所使用的虚拟地址空间都是一样的,比如 0x0000 ~ 0xffff,但他们的虚拟地址会被映射到主存上的不同区域。从程序的角度来看,它觉得自己独享了一整块内存。不用考虑访问冲突的问题。
  2. 可以使得进程的运行内存超过物理内存大小,因为程序运行符合局部性原理,CPU访问内存会有很明显的重复访问的倾向性,对于那些没有被经常使用到的内存,我们可以把它换出到物理内存之外,比如硬盘上的 swap 区域。

虚拟地址翻译

虚拟内存的实现方式,大多数都是通过页表来实现的。操作系统虚拟内存空间分成一页一页的来管理,每页的大小为 4K。页表被操作系统放在物理内存的指定位置,CPU 上有个 Memory Management Unit(MMU) 单元,CPU 把虚拟地址给 MMU,MMU 去物理内存中查询页表,得到实际的物理地址

2024-11-24T011131
为了解决简单分页产生的页表过大的问题,就有了多级页表,它解决了空间上的问题,但这就会导致CPU在寻址的过程中,需要有很多层表参与,加大了时间上的开销。于是根据程序的局部性原理,在CPU芯片中加入了TLB,负责缓存最近常被访问的页表项,大大提高了地址的转换速度。

Linux内存布局

2024-11-24T011314

  • 栈段,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是8 MB
  • 文件映射段,包括动态库、共享内存等;
  • 堆段,包括动态分配的内存,从低地址开始向上增长;
  • BSS段,包括未初始化的静态变量和全局变量;
  • 数据段,包括已初始化的静态常量和全局变量;
  • 代码段,包括二进制可执行代码;

  • 进程在用户态时,只能访问用户空间内存;
  • 只有进入内核态后,才可以访问内核空间的内存;
    虽然每个进程都各自有独立的虚拟内存,但是每个虚拟内存中的内核地址,其实关联的都是相同的物理内存。这样,进程切换到内核态后,就可以很方便地访问内核空间内存。

栈空间是通过压栈出栈方式自动分配释放的,由系统管理,使用起来高效无感知。

堆空间是用以动态分配的,由程序自己管理分配和释放。Go 语言虽然可以帮我们自动管理分配和释放,但是代价也是很高的。

总结

  • 虚拟内存本质上是将磁盘当成最终存储,而主存作为了一个 cache
  • 局部性好的程序,可以提高缓存命中率,这对底层系统的内存管理是很友好的
  • 整个计算机体系里面,缓存是无处不在的,整个计算机体系就是建立在一级级的缓存之上的

发散思考

  • 手机内存拓展(内存融合)技术、windows虚拟内存技术 本质就是把虚拟内存技术的应用,把虚拟地址映射到磁盘上,以此扩充内存空间,但是磁盘的速度比内存慢得多,在内存充足的情况下反而可能会拖慢性能表现。
  • 游戏服务器ECS(Entity Component System)架构,充分利用局部性原理,提高效率。