《现代体系结构上的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)。这种功能可以让内核将正文页映射为只读的,同时允许对数据页的读写访问。

相关文章
|
3月前
|
数据采集 存储 缓存
如何使用缓存技术提升Python爬虫效率
如何使用缓存技术提升Python爬虫效率
|
3月前
|
存储 缓存 负载均衡
从零到一:分布式缓存技术初探
分布式缓存通过将数据存储在多个节点上,利用负载均衡算法提高访问速度、降低数据库负载并增强系统可用性。常见产品有Redis、Memcached等。其优势包括性能扩展、高可用性、负载均衡和容错性,适用于页面缓存、应用对象缓存、状态缓存、并行处理、事件处理及极限事务处理等多种场景。
296 1
|
5月前
|
存储 缓存 数据库
缓存技术有哪些应用场景呢
【10月更文挑战第19天】缓存技术有哪些应用场景呢
|
5月前
|
存储 缓存 运维
缓存技术有哪些优缺点呢
【10月更文挑战第19天】缓存技术有哪些优缺点呢
|
6月前
|
存储 缓存 NoSQL
解决Redis缓存击穿问题的技术方法
解决Redis缓存击穿问题的技术方法
109 2
|
6月前
|
存储 缓存 Java
在Spring Boot中使用缓存的技术解析
通过利用Spring Boot中的缓存支持,开发者可以轻松地实现高效和可扩展的缓存策略,进而提升应用的性能和用户体验。Spring Boot的声明式缓存抽象和对多种缓存技术的支持,使得集成和使用缓存变得前所未有的简单。无论是在开发新应用还是优化现有应用,合理地使用缓存都是提高性能的有效手段。
94 1
|
7月前
|
缓存 NoSQL Java
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
Spring Cache 是 Spring 提供的简易缓存方案,支持本地与 Redis 缓存。通过添加 `spring-boot-starter-data-redis` 和 `spring-boot-starter-cache` 依赖,并使用 `@EnableCaching` 开启缓存功能。JetCache 由阿里开源,功能更丰富,支持多级缓存和异步 API,通过引入 `jetcache-starter-redis` 依赖并配置 YAML 文件启用。Layering Cache 则提供分层缓存机制,需引入 `layering-cache-starter` 依赖并使用特定注解实现缓存逻辑。
1693 1
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
|
8月前
|
存储 缓存 算法
深入了解Memcached:缓存技术的利器
Memcached是一个开源的高性能分布式内存缓存系统,用于加速动态Web应用。它通过将数据库查询结果、API调用结果或其他数据缓存到内存中,减少对数据库的访问频率,从而提高应用的响应速度。本文详细介绍了Memcached的基本原理、架构、安装配置、使用方法、测试方法以及应用场景。通过Memcached,开发者可以有效提升Web应用的性能,减少数据库负载,改善用户体验。
149 5
|
7月前
|
存储 缓存 NoSQL
【性能飙升的秘密】FastAPI应用如何借助缓存技术实现极速响应?揭秘高效Web开发的制胜法宝!
【8月更文挑战第31天】FastAPI是一个高性能Web框架,利用Starlette和Pydantic实现高效API构建。本文介绍如何通过缓存提升FastAPI应用性能,包括使用`starlette-cache[redis]`实现Redis缓存,以及缓存一致性和缓存策略的注意事项。通过具体示例展示了缓存的配置与应用,帮助开发者构建更高效的Web应用。
450 0
|
8月前
|
Java Linux 编译器
【Linux】深挖进程地址空间
【Linux】深挖进程地址空间
56 0