独享内存空间的原理

每个项目的物理地址对于进程不可见,谁也不能直接访问这个物理地址。操作系统会给进程分配一个虚拟地址。所有进程看到的这个地址都是一样的,里面的内存都是从 0 开始编号。

当程序要访问虚拟地址的时候,由内核的数据结构进行转换,转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,这样就不会冲突了。

规划虚拟地址空间

操作系统的内存管理,主要分为三个方面:

  • 第一,物理内存的管理,相当于会议室管理员管理会议室。
  • 第二,虚拟地址的管理,也即在项目组的视角,会议室的虚拟地址应该如何组织。
  • 第三,虚拟地址和物理地址如何映射,也即会议室管理员如何管理映射表。

内核与用户使用什么地址:

  • 用户态的进程使用虚拟地址
  • 内核态的也基本都是使用虚拟地址

如果是 32 位,有 2^32 = 4G 的内存空间都是我的,不管内存是不是真的有 4G。如果是 64 位,在 x86_64 下面,其实只使用了 48 位,那也挺恐怖的。48 位地址长度也就是对应了 256TB 的地址空间。我都没怎么见过 256T 的硬盘,别说是内存了。

  • 首先,这么大的虚拟空间一切二,一部分用来放内核的东西,称为内核空间
  • 一部分用来放进程的东西,称为用户空间。用户空间在下,在低地址

image.png

  • Text Segment 是存放二进制可执行代码的位置
  • Data Segment 存放静态常量
  • BSS Segment 存放未初始化的静态变量。
  • 堆(Heap)段。堆是往高地址增长的,是用来动态分配内存的区域
  • Memory Mapping Segment。这块地址可以用来把文件映射进内存用的,如果二进制的执行文件依赖于某个动态链接库,就是在这个区域里面将 so 文件映射到了内存中。
  • 栈(Stack)地址段。主线程的函数调用的函数栈就是用这里的。

如果普通进程还想进一步访问内核空间,是没办法的,只能眼巴巴地看着。如果需要进行更高权限的工作,就需要调用系统调用,进入内核。

一旦进入了内核,就换了一种视角。刚才是普通进程的视角,觉着整个空间是它独占的,没有其他进程存在。

但是到了内核里面,无论是从哪个进程进来的,看到的都是同一个内核空间,看到的都是同一个进程列表。虽然内核栈是各用各的,但是如果想知道的话,还是能够知道每个进程的内核栈在哪里的。所以,如果要访问一些公共的数据结构,需要进行锁保护。

image.png

内核的代码访问内核的数据结构,大部分的情况下都是使用虚拟地址的,虽然内核代码权限很大,但是能够使用的虚拟地址范围也只能在内核空间,也即内核代码访问内核数据结构。

精彩评论

  • 所有进程的内核空间是共用一块内存