[操作系统] 进程地址空间管理

虚拟地址空间的初始化

缺页中断

缺页中断的概念

缺页中断(Page Fault Interrupt) 是指当程序访问的虚拟地址在页表中不存在有效映射(即该页未加载到内存中)时,CPU 会发出一个中断信号,请求操作系统加载所需的页面到内存。
这是现代操作系统实现 虚拟内存管理 的重要机制之一。


缺页中断的触发条件

缺页中断通常在以下情况下触发:

  1. 虚拟地址对应的页面不在内存中:
    • 页表中找不到对应的物理页帧(页表条目为空或无效)。
    • 常见于程序访问未加载到内存的代码段或数据段。
  2. 访问非法地址:
    • 程序试图访问一个不存在的虚拟地址(如超出地址空间范围)。
    • 操作系统会判断访问是否合法,非法访问将触发异常。

缺页中断的处理流程

以下是缺页中断的详细处理流程:

  1. 程序访问虚拟地址:
    • CPU 将虚拟地址拆分为页号和页内偏移量,根据页号查询页表。
  2. 检测页表:
    • 如果页表中没有找到对应的物理页帧(即页表项无效),触发缺页中断。
  3. 陷入内核:
    • 缺页中断引发陷入操作系统内核,操作系统负责处理。
  4. 检查页面是否合法:
    • 操作系统检查虚拟地址是否属于当前进程的合法地址范围:
      • 如果地址非法(如访问未分配的堆空间),操作系统会终止进程,抛出段错误(Segmentation Fault)。
      • 如果合法,进入下一步。
  5. 分配页面:
    • 操作系统为该虚拟页分配物理页帧。
    • 如果内存不足,则触发页面置换算法(如LRU、FIFO),将某些页面换出到硬盘(即交换分区或页面文件)。
  6. 加载页面:
    • 如果访问的页面是磁盘文件的一部分(如代码或数据),则将页面从磁盘加载到内存。
    • 将分配的物理页帧的地址填入页表,并将页表项标记为有效。
  7. 恢复程序执行:
    • 操作系统返回用户态,重新执行导致缺页的指令。
    • 由于页面已加载到内存,访问可以正常完成。

缺页中断的示例

假设一个程序试图访问数组元素,但数组较大,未全部加载到内存。以下是可能的情景:

int arr[100000]; // 数组在内存中未完全加载
for (int i = 0; i < 100000; i++) {arr[i] = i;  // 顺序访问
}
  1. 程序首次访问 arr[i] 时,CPU 查询页表,发现对应的虚拟页面未映射到物理内存,触发缺页中断。
  2. 操作系统加载对应页面到内存,并更新页表。
  3. 下一次访问已加载的页面时,程序可以直接读取,无需触发缺页中断。

示例总结

当一个应用程序数据过大的话,不会立即将所有的数据全部从硬盘上加载到物理内存中,会先加载一部分。但是在进程的虚拟地址空间中会将所有的数据对应的地址全部建立。于是当需要使用一个虚拟地址空间的时候会在页表中进行查找映射的物理地址,但是没有物理地址,还未加载进内存中。操作系统会动态加载数据,当需要的时候再申请物理空间,加载数据,然后建立映射关系。

即使加载到物理内存的数据是乱序存储的,通过页表的映射关系也可以进行有序的管理。

虚拟地址空间初始化

虚拟地址空间就是mm_struct,在task_struct中。作为对象,需要被初始化。

当一个新进程被创建(例如通过 forkexec 系统调用)时,操作系统会:

  1. 创建虚拟地址空间:
    • 为进程分配独立的虚拟地址空间,确保不同进程之间的地址空间隔离。
  2. 设置页表:
    • 页表初始状态为“未映射”(即页面不在物理内存中),以支持按需加载。
  3. 加载程序的基础信息:
    • 通过程序文件(如 ELF 文件)中的头部信息,划分代码段、数据段等区域。
    • 堆区和栈区只初始化元信息(如起始地址),实际分配时动态增长。
    • 所以大部分会在程序加载的时候就被初始化。

为什么要有进程地址空间

将地址从无需变有序

数据从磁盘加载到物理内存是动态加载的,顺序会变得无规则,甚至乱序。但是有了虚拟地址空间和页表,虚拟地址空间中各个区域的地址是有序的,然后通过页表进行映射,找到无序的物理内存地址,从而将物理地址进行有序管理。

地址转换时的合法性判定

当地址转换的时候,通过虚拟地址空间和页表可以对地址和操作进行合法性判定,防止直接操作物理内存造成损坏,进而保护物理内存

页表中对于每一个映射关系也会有权限(rwx...)存在,当进程又不合法操作的时候,操作系统会拒绝地址映射转换,甚至杀死进程。

野指针

从操作系统层面理解野指针:

  • 未初始化指针与页表:当一个指针未初始化时,它指向的虚拟地址是随机的。这个随机地址很可能在页表中没有对应的映射项。因为正常的内存分配(如通过mallocnew等操作)会由操作系统分配一段合法的虚拟地址,并在页表中建立映射。而未初始化的指针所指向的地址没有经过这样的分配过程,所以在页表中找不到对应的物理地址。
  • 已释放指针与页表:指针所指向的内存被释放后,操作系统会将这块内存对应的页表项标记为未使用或者分配给其他进程。如果继续使用这个指针访问内存,操作系统在查找页表时会发现这个虚拟地址对应的页表项已经不再有效。例如,一个动态分配的内存块被deletefree后,它所占用的虚拟地址范围在页表中的映射会被撤销或者改变,再次访问这个地址就会引发错误。
  • 越界指针与页表:当指针越界时,它可能会指向虚拟地址空间中未分配的区域。比如,一个数组指针越界后指向了数组之外的地址,这个地址可能超出了操作系统为该数组分配的合法虚拟地址范围。在页表中,这个越界的地址没有合法的物理地址映射,因为操作系统只会在合法的内存分配范围内建立页表映射。
  • 错误赋值指针与页表:如果指针被错误地赋值为一个非法的地址,这个地址在页表中很可能没有对应的映射。因为操作系统在进行正常的内存分配时,会确保分配的虚拟地址在页表中有合法的映射。而人为错误地给指针赋一个非法地址,打破了这种正常的映射关系。

查页表失败后,会反馈给操作系统,操作系统会处理进程,所以野指针会导致操作系统杀死进程,导致进程崩溃。

字符串常量为什么无法修改

常量字符串字面量(如<font style="color:rgb(6, 6, 7);">char* ptr = "Hello, World!"</font>)通常被存储在代码段(Text Segment)中。这是因为常量字符串在程序的整个运行过程中不需要修改,将它们放在代码段可以利用代码段的只读特性来保护这些字符串不被意外修改。

这就是在地址转换的时候权限拒绝了对数据的写操作,所以无法修改。

解耦合!

简单回顾一下程序加载进内存的过程:

  1. 在虚拟地址空间申请指定大小的空间(调整区域划分)。
  2. 加载程序,申请物理空间。
  3. 在页表中进行虚拟地址和物理地址的映射构建。

完成后,此时的物理地址就转化为了虚拟地址。提供给上层使用,用户无需关心底层的物理地址是什么,物理内存中是如何加载的,只是使用虚拟地址就可以了。

作为进程也只是关心对于虚拟地址的使用,而不关心实际物理内存的存储。

如图所示。task_struct和其中管理的mm_struct所形成的关系对于进程来说只是负责进程的调度,而不关心如何管理调度的数据存储和加载。

而对于物理内存部分来说,只进行内存的管理,加载物理内存。

二者通过页表的映射关系来解耦合,让进程管理和内存管理进行一定程度的解耦合!

Tips

  1. 可以不加载代码和数据,只加载task_structmm_struct(只拿到main代码的起始地址),页表。

CPU在拿到起始地址后,当访问虚拟地址时,会有标识证明没有加载过物理内存,所以会缺页中断,然后开始加载物理内存。

  1. 创建进程的时候,先有task_struct,mm_struct等,还是先加载代码和数据?

先有内核数据结构,然后再陆续加载物理内存。

  1. 如何理解进程挂起?

进程进入挂起状态时,操作系统找到对应的进程,清空页表的物理地址部分,将物理地址对应的数据全部换入磁盘swap分区。只保留虚拟地址空间中的虚拟地址和页表的虚拟地址部分。

当挂起状态结束时,将swap分区的数据全部换出加载到物理内存中,然后再页表中建立映射。这就是解耦的好处,将进程调度与内存管理完全解耦。

vm_area_struct

对于在程序中动态申请的空间来说,一般会申请在堆区中。但是在程序中可能会频繁的申请,难道对于虚拟地址空间中的堆区来说,不只有一个起始地址吗?可能是离散分布的吗?

虚拟空间的组织⽅式有两种:

  1. 当虚拟区较少时采取单链表,由mmap指针指向这个链表;
  2. 当虚拟区间多时采取红⿊树进⾏管理,由mm_rb指向这棵树。

mm_struct 并不是直接就是整个虚拟地址空间,而是包含了一个指向虚拟内存区域(VMA)列表的指针,这个列表是由 vm_area_struct组成的。每个 vm_area_struct 表示进程地址空间中的一个连续区域,具有相同的权限和映射类型。所以离散申请的堆空间可以用vm_area_struct进行管理,并且所有的区域都可以统一使用vm_area_struct进行管理。

为了高效查找区域,所以用红黑树来进行管理——struct rb_root mm_rb

struct mm_struct {// ... 其他成员 ...struct vm_area_struct *mmap; // 指向虚拟内存区域列表的指针struct rb_root mm_rb;         // 红黑树,用于快速查找虚拟内存区域// ... 其他成员 ...
};
struct vm_area_struct {struct mm_struct *vm_mm;         // 指向所属进程的mm_structunsigned long vm_start;          // 虚拟内存区域的起始地址unsigned long vm_end;            // 虚拟内存区域的结束地址pgprot_t vm_page_prot;           // 页面保护标志unsigned long vm_flags;           // 标志位,如VM_READ, VM_WRITE等// ... 其他成员,如链表指针、红黑树节点等 ...
};

因为存在vm_area_struct这个数据结构,即使堆区频繁申请,但是每一段申请的空间都可以使用vm_area_struct来进行管理,最后用建表进行管理所有的vm_area_struct,在mm_struct中用*mmap作为链表的头指针。

进程地址空间管理(总结)

关于进程地址空间整体的管理结构如上图所示(虚拟区间较少情况下)。

task_struct管理整体进程,其中包括管理进程地址空间的mm_struct。在虚拟区间较少的情况下用在mm_struct中的vm_area_struct *mmap;指向由vm_area_struct链接而成的链表。每个vm_area_struct对应一段虚拟区间,而对于堆这种可能频繁申请的来说,堆这个区间会由多个vm_area_struct组成,而其他的区域使用一个vm_area_struct管理即可。

为什么要有进程地址空间(总结)

在早期的计算机中,程序直接操作物理内存,所有内存地址都是物理地址。当同时运行多个程序时,内存管理必须确保程序使用的内存总量小于物理内存。然而,这种直接操作物理内存的方式存在以下问题:


安全风险
  • 直接操作物理内存允许每个进程访问任意内存空间。
  • 恶意程序(如木马病毒)可以随意修改系统内存区域,导致设备瘫痪。

地址不确定性
  • 编译完成的程序存储在硬盘上,运行时需加载到内存。
  • 由于内存分配动态变化,程序的物理内存地址在不同运行时可能不同。
    • 例如:第一次运行时,程序加载到地址0x00000000
    • 第二次运行时,内存已被占用,加载地址可能变为其他位置。

效率低下
  • 使用物理内存时,进程以整体内存块操作。
  • 当物理内存不足时,将进程从内存拷贝到磁盘(交换分区)需要拷贝整个进程,耗时较长,效率低下。

虚拟地址空间与分页机制的优势

1. 内存安全
  • 地址空间和页表由操作系统创建和维护。
  • 所有地址空间和页表映射必须经过操作系统监管。
  • 保护物理内存中的合法数据,防止非法访问。
2. 地址灵活性
  • 地址空间和页表映射机制允许物理内存中的数据以任意位置加载。
  • 内存分配与进程管理解耦,进程不再直接依赖物理内存地址。
3. 延迟分配
  • 在C/C++中,通过newmalloc申请的空间实际上分配在虚拟地址空间。
  • 物理内存分配延迟到真正访问内存时执行。
  • 操作系统自动完成内存分配和页表映射,用户或进程无需感知。
4. 提高效率
  • 程序在物理内存中的加载可以是任意位置,通过页表实现虚拟地址与物理地址的映射。
  • 虚拟地址空间使得进程内存分布在逻辑上保持有序,简化了程序管理。

总结

虚拟地址空间通过操作系统的地址空间管理和页表机制,解决了直接操作物理内存带来的安全性、灵活性和效率问题,使得内存管理更安全、更高效,同时简化了程序开发与运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/7641.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

万字长文总结前端开发知识---JavaScriptVue3Axios

JavaScript学习目录 一、JavaScript1. 引入方式1.1 内部脚本 (Inline Script)1.2 外部脚本 (External Script) 2. 基础语法2.1 声明变量2.2 声明常量2.3 输出信息 3. 数据类型3.1 基本数据类型3.2 模板字符串 4. 函数4.1 具名函数 (Named Function)4.2 匿名函数 (Anonymous Fun…

【Linux】21.基础IO(3)

文章目录 3. 动态库和静态库3.1 静态库与动态库3.2 静态库的制作和使用原理3.3 动态库的制作和使用原理3.3.1 动态库是怎么被加载的 3.4 关于地址 3. 动态库和静态库 3.1 静态库与动态库 静态库&#xff08;.a&#xff09;&#xff1a;程序在编译链接的时候把库的代码链接到可…

Linux系统之gzip命令的基本使用

Linux系统之gzip命令的基本使用 一、gzip命令简介二、gzip命令使用帮助2.1 help帮助信息2.2 选项解释 三、gzip命令的基本使用3.1 压缩文件3.2 保留原始文件3.3 解压文件3.4 查看压缩信息3.5 标准输出/输入3.6 批量处理文件3.7 递归解压缩目录3.8测试压缩文件完整性 四、注意事…

【Matlab高端绘图SCI绘图模板】第05期 绘制高阶折线图

1.折线图简介 折线图是一个由点和线组成的统计图表&#xff0c;常用来表示数值随连续时间间隔或有序类别的变化。在折线图中&#xff0c;x 轴通常用作连续时间间隔或有序类别&#xff08;比如阶段1&#xff0c;阶段2&#xff0c;阶段3&#xff09;。y 轴用于量化的数据&#x…

免费SSL证书申请,springboot 部署证书

申请免费域名证书,SSL证书(一共有两个有用&#xff0c;一个是私钥private.key 另一个是certificate.crt) 1、打开网址 申请免费域名证书,SSL证书 2、选择生成CSR 3、生成以后点击下一步&#xff08;private key 有用 ) ​​​​​​​ 4、这里选择 Cname域名解析验证 5、…

Java 大视界 -- Java 大数据中的隐私增强技术全景解析(64)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

深度学习项目--基于LSTM的糖尿病预测探究(pytorch实现)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 LSTM模型一直是一个很经典的模型&#xff0c;一般用于序列数据预测&#xff0c;这个可以很好的挖掘数据上下文信息&#xff0c;本文将使用LSTM进行糖尿病…

js/ts数值计算精度丢失问题及解决方案

文章目录 概念及问题问题分析解决方案方案一方案二方案其它——用成熟的库 概念及问题 js中处理浮点数运算时会出现精度丢失。js中整数和浮点数都属于Number数据类型&#xff0c;所有的数字都是以64位浮点数形式存储&#xff0c;整数也是如此。所以打印x.00这样的浮点数的结果…

vite环境变量处理

环境变量: 会根据当前代码环境产生值的变化的变量就叫做环境变量 代码环境: 开发环境测试环境预发布环境灰度环境生产环境 举例: 百度地图 SDK,小程序的SDK APP_KEY: 测试环境和生产环境还有开发环境是不一样的key 开发环境: 110 生产环境:111 测试环境: 112 我们去请求第三…

Android GLSurfaceView 覆盖其它控件问题 (RK平台)

平台 涉及主控: RK3566 Android: 11/13 问题 在使用GLSurfaceView播放视频的过程中, 增加了一个播放控制面板, 覆盖在视频上方. 默认隐藏setVisibility(View.INVISIBLE);点击屏幕再显示出来. 然而, 在RK3566上这个简单的功能却无法正常工作. 通过缩小视频窗口可以看到, 实际…

Linux 环境变量

目录 一、环境变量的基本概念 1.常见环境变量 2.查看环境变量方法 ​3.几个环境变量 环境变量&#xff1a;PATH 环境变量&#xff1a;HOME 环境变量&#xff1a;SHELL 二、和环境变量相关的命令 三、库函数getenv&#xff0c;setenv 四、环境变量和本地变量 五、命令行…

Redis实战(黑马点评)——涉及session、redis存储验证码,双拦截器处理请求

项目整体介绍 数据库表介绍 基于session的短信验证码登录与注册 controller层 // 获取验证码PostMapping("code")public Result sendCode(RequestParam("phone") String phone, HttpSession session) {return userService.sendCode(phone, session);}// 获…

MYSQL数据库 - 启动与连接

MYSQL数据库的启动&#xff1a; 一 在cmd控制界面以管理员身份运行 执行语句: net start mysql80 net stop mysql80 二 MYSQL数据库客户端建立连接&#xff1a; 1 该种方法是使用windows系统的cmd界面&#xff0c;需要配置相关路径path 2 使用MYSQL自带的

【Salesforce】审批流程,代理登录 tips

审批流程权限 审批流程权限问题解决方案代理登录代理登录后Logout 审批流程权限 前几天&#xff0c;使用审批流程&#xff0c;但是是两个sandbox&#xff0c;同样的配置&#xff0c;我有管理员权限。但是profile不是管理员&#xff0c;只是通过具备管理员权限的permission set…

RDMA 工作原理 | 支持 RDMA 的网络协议

注&#xff1a;本文为 “RDMA” 相关文章合辑。 英文引文机翻未校。 图片清晰度受引文所限。 Introduction to Remote Direct Memory Access (RDMA) Written by: Dotan Barak on March 31, 2014.on February 13, 2015. What is RDMA? 什么是 RDMA&#xff1f; Direct me…

hexo + Butterfly搭建博客

Hexo‌是一个基于Node.js的静态网站生成器&#xff0c;主要用于快速搭建博客和个人网站。它使用Markdown语法编写文章&#xff0c;能够迅速生成静态页面并部署到服务器上。 配置node 使用nvm安装node(v16.13.2)后配置镜像 安装并使用node&#xff1a; nvm install 16.13.2 n…

手撕B-树

一、概述 1.历史 B树&#xff08;B-Tree&#xff09;结构是一种高效存储和查询数据的方法&#xff0c;它的历史可以追溯到1970年代早期。B树的发明人Rudolf Bayer和Edward M. McCreight分别发表了一篇论文介绍了B树。这篇论文是1972年发表于《ACM Transactions on Database S…

【2025年数学建模美赛F题】(顶刊论文绘图)模型代码+论文

全球网络犯罪与网络安全政策的多维度分析及效能评估 摘要1 Introduction1.1 Problem Background1.2Restatement of the Problem1.3 Literature Review1.4 Our Work 2 Assumptions and Justifications数据完整性与可靠性假设&#xff1a;法律政策独立性假设&#xff1a;人口统计…

【FreeRTOS 教程 四】队列创建与发布项目到队列

目录 一、FreeRTOS队列&#xff1a; &#xff08;1&#xff09;队列介绍&#xff1a; &#xff08;2&#xff09;用户模型说明&#xff1a; &#xff08;3&#xff09;阻塞队列&#xff1a; 二、队列管理 API&#xff1a; &#xff08;1&#xff09;uxQueueMessagesWaiti…

如何在data.table中处理缺失值

&#x1f4ca;&#x1f4bb;【R语言进阶】轻松搞定缺失值&#xff0c;让数据清洗更高效&#xff01; &#x1f44b; 大家好呀&#xff01;今天我要和大家分享一个超实用的R语言技巧——如何在data.table中处理缺失值&#xff0c;并且提供了一个自定义函数calculate_missing_va…