独享内存空间的原理
每个项目的物理地址对于进程不可见,谁也不能直接访问这个物理地址。操作系统会给进程分配一个虚拟地址。所有进程看到的这个地址都是一样的,里面的内存都是从 0 开始编号。
当程序要访问虚拟地址的时候,由内核的数据结构进行转换,转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,这样就不会冲突了。
规划虚拟地址空间
操作系统的内存管理,主要分为三个方面:
- 第一,物理内存的管理,相当于会议室管理员管理会议室。
- 第二,虚拟地址的管理,也即在项目组的视角,会议室的虚拟地址应该如何组织。
- 第三,虚拟地址和物理地址如何映射,也即会议室管理员如何管理映射表。
内核与用户使用什么地址:
- 用户态的进程使用虚拟地址
- 内核态的也基本都是使用虚拟地址
如果是 32 位,有 2^32 = 4G 的内存空间都是我的,不管内存是不是真的有 4G。如果是 64 位,在 x86_64 下面,其实只使用了 48 位,那也挺恐怖的。48 位地址长度也就是对应了 256TB 的地址空间。我都没怎么见过 256T 的硬盘,别说是内存了。
- 首先,这么大的虚拟空间一切二,一部分用来放内核的东西,称为内核空间
- 一部分用来放进程的东西,称为用户空间。用户空间在下,在低地址
- Text Segment 是存放二进制可执行代码的位置
- Data Segment 存放静态常量
- BSS Segment 存放未初始化的静态变量。
- 堆(Heap)段。堆是往高地址增长的,是用来动态分配内存的区域
- Memory Mapping Segment。这块地址可以用来把文件映射进内存用的,如果二进制的执行文件依赖于某个动态链接库,就是在这个区域里面将 so 文件映射到了内存中。
- 栈(Stack)地址段。主线程的函数调用的函数栈就是用这里的。
如果普通进程还想进一步访问内核空间,是没办法的,只能眼巴巴地看着。如果需要进行更高权限的工作,就需要调用系统调用,进入内核。
一旦进入了内核,就换了一种视角。刚才是普通进程的视角,觉着整个空间是它独占的,没有其他进程存在。
但是到了内核里面,无论是从哪个进程进来的,看到的都是同一个内核空间,看到的都是同一个进程列表。虽然内核栈是各用各的,但是如果想知道的话,还是能够知道每个进程的内核栈在哪里的。所以,如果要访问一些公共的数据结构,需要进行锁保护。
内核的代码访问内核的数据结构,大部分的情况下都是使用虚拟地址的,虽然内核代码权限很大,但是能够使用的虚拟地址范围也只能在内核空间,也即内核代码访问内核数据结构。
精彩评论
- 所有进程的内核空间是共用一块内存