《现代体系结构上的UNIX系统:内核程序员的对称多处理和缓存技术(修订版)》——1.3 进程地址空间-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

《现代体系结构上的UNIX系统:内核程序员的对称多处理和缓存技术(修订版)》——1.3 进程地址空间

简介: 内核给每个进程提供了它自己的虚拟地址空间(virtual address space)。在正常情况下,一个进程不能直接访问另一个进程的地址空间;这就提供了一种高度的保护能力,防止来自系统中其他正在执行的进程的干扰。有些实现提供了允许内存的部分区域被多个进程所共享的机制。

本节书摘来自异步社区《现代体系结构上的UNIX系统:内核程序员的对称多处理和缓存技术(修订版)》一书中的第1章,第1.2节,作者:【美】Curt Schimmel著,更多章节内容可以访问云栖社区“异步社区”公众号查看

1.3 进程地址空间

内核给每个进程提供了它自己的虚拟地址空间(virtual address space)。在正常情况下,一个进程不能直接访问另一个进程的地址空间;这就提供了一种高度的保护能力,防止来自系统中其他正在执行的进程的干扰。有些实现提供了允许内存的部分区域被多个进程所共享的机制。共享内存(shared memory)和映射文件(mapped file)机制都将在本章后面的内容中进行讨论。其他机制(比如vfork系统调用和线程)都能在进程间共享部分或者全部地址空间。对于本书的目的来说,这类机制都具有和共享内存同样的特点,所以就不再深入讨论了。几乎所有的UNIX系统实现都使用请求调页机制(demand paging)来管理物理内存的分配。

一个进程的地址空间由4个主要部分构成:程序指令、初始化数据、未初始化数据和栈。在UNIX的行话中,指令(instruction)也叫做“正文”段,而初始化数据和栈可以分别简称为“数据”段和“栈”段。未初始化数据则叫做“bss”,它的名字来源于一种叫做“Block Started by Symbol”的古老的汇编程序助记符,这个助记符用于分配未初始化的数据空间。初始化数据和未初始化数据之间的区别在于,初始化数据是在程序编译时已经声明有一个初始值的全局和静态程序变量。未初始化数据是没有明确初始值的全局和静态程序变量。对于这些数据,UNIX系统仅仅依照C程序设计语言(UNIX系统几乎都是用这种语言编写的)的语义在地址空间中分配初始包含0的内存。这种方法的优点是未初始化数据不需要在程序文件中占用空间。

大多数使用32位虚拟地址的系统都在用户程序和内核之间划分整个4 GB的地址空间。虽然每个段的实际起始地址是与实现无关的,但是典型的布局则如图1-2所示。通常低2 GB空间供用户使用,被称为用户地址空间(user address space)。高2 GB空间则为内核保留,不准用户级代码读写,这是内核地址空间(kernel address space)。内核地址空间包括内核的正文和数据结构。当内核正在执行的时候,它可以访问整个地址空间。这种安排易于让内核在其代表用户进程执行一次系统调用的时候在用户进程的地址空间中运行。
screenshot

用户正文和数据段的大小在程序编译时就固定了,它们只能在执行程序的时候从包含程序的文件中复制到地址空间里。bss段和栈段能够在运行期动态地增长,在它们之间有一段未用的虚拟地址空间来调节增长的空间。在本书中,栈段始终是向较低的内存地址增长的,对于大多数计算机系统来说都是这样。

bss段能够借助sbrk系统调用增长或者缩小。bss段只能向较高的内存地址增长。栈根据需要由内核来动态和透明地增长。当试图访问当前分配的栈段以下的未用区时,就发生了一次缺页错误(page fault)。内核检查栈指针寄存器的内容,如果它包含的地址比栈段顶部当前的地址要小,那么内核就扩大栈段,把栈指针寄存器中的地址包括进来,并且重新执行导致缺页错误的操作。

其他类型的段,比如共享库和共享内存,都可以包含在用户地址空间内。共享库包括附加的正文、数据和bss段,它们用于常用的函数和服务。共享内存则在本章的后面介绍。

1.3.1 地址空间映射
内核负责将一个进程的虚拟地址空间映射到计算机的物理地址空间上。大多数计算机允许任何虚拟页面被映射到存储器中的任何物理页面上。例如,一个进程的虚拟地址空间可以按照图1-3所示进行映射。

screenshot

这幅图中的箭头显示了该进程内的一个虚拟页面被映射到了哪个物理页面上。于是,比如说如果进程访问虚拟页面2,那么对该页面的引用将被映射到物理页面1上。从虚拟地址空间到物理地址空间的映射是由内存管理单元(Memory Management Unit,MMU)来执行的,MMU负责进程所使用的全部地址。

每一个进程都有自己的与其关联的映射关系,并且作为进程上下文的一部分来保存。在进程运行的时候,内核将进程映射关系的描述提供给MMU。

注意,不是所有的虚拟页面都需要映射。例如,图1-3中的虚拟页面1、5和8就没有被映射到任何物理页面上。它们表示进程的地址空间中没有使用的页面,也可以是当前没有驻留在内存中的页面。如果一个进程试图访问后一种类型的页面,那么内核就要把相关的物理页面调入到内存中,并且把虚拟页面映射到新分配的物理页面上。

同样,不是存储器中所有的物理页面都由某个进程来使用。在图1-3中的例子里,物理页面2、4和8没有从这个进程到它们那里的映射关系。当前正在执行的进程将无法访问它们。这些物理页面可以属于系统中的其他进程,或者干脆就没有使用。无论是哪一种情况,肯定都不允许当前正在执行的进程访问它们。

通过把各个进程中的虚拟页面映射到同一个物理页面上,内核可以让多个进程共享特定的物理页面(这将在以后更详细地进行讨论)。

大多数MMU都有给每个映射关系关联一个访问权限(access permission)的能力。最常用的两种权限是读(read)和写(write)。这种功能可以让内核将正文页映射为只读的,同时允许对数据页的读写访问。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: