前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【Linux篇】探索进程地址空间:计算机背后的虚拟世界

【Linux篇】探索进程地址空间:计算机背后的虚拟世界

作者头像
熬夜学编程的小王
发布于 2025-04-04 01:33:42
发布于 2025-04-04 01:33:42
10200
代码可运行
举报
文章被收录于专栏:编程小王编程小王
运行总次数:0
代码可运行
进程地址空间的奥秘:让你理解程序如何在计算机中生存 本文将介绍进程地址空间的基本概念与结构,帮助读者理解操作系统如何管理和分配内存。进程地址空间指的是操作系统为每个运行的进程分配的内存区域,包括代码段、数据段、堆区、栈区等。这些区域各自有特定的功能与管理方式。代码段用于存放程序执行指令,数据段用于存放全局变量,堆区用于动态分配内存,而栈区则用于存储函数调用时的局部变量和返回地址。通过了解这些内存区域的作用与分配机制,读者能更好地理解操作系统如何保障多进程运行时的内存安全与隔离性。

💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力! 👍 点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力! 🚀 分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对Linux OS感兴趣的朋友,让我们一起进步!

一. 程序地址空间

1.1 基本概念

虚拟地址空间是指操作系统为每个进程提供的一个独立的内存空间,它与实际物理内存分离。每个进程都拥有自己的虚拟地址空间,操作系统通过内存管理单元(MMU)将虚拟地址映射到物理内存地址,实现了进程间内存的隔离和保护。虚拟地址空间使得每个进程似乎拥有从零开始的连续内存,避免了直接访问物理内存的复杂性,同时提供了更高的灵活性和安全性。

下面来段代码,来验证确实有虚拟地址的存在!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 0;
}
else if(id == 0){ //child,⼦进程肯定先跑完,也就是⼦进程先修改,完成之后,⽗进程再
读取
g_val=100;
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
}else{ //parent
sleep(3);
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
sleep(1);
return 0;
}

输出结果:

child[3046]: 100 : 0x80497e8 parent[3045]: 0 : 0x80497e8

通过现象可以看出,全局变量g_val在父子进程中值不相同,地址却相同。这里的地址就是虚拟地址,为什么值不一样,因为每个进程都会有独立的虚拟地址空间和一套页表(该页表作用:建立虚拟地址与物理地址的映射关系),OS会将虚拟地址转化为物理地址。

图来理解该过程更形象,如下图:

结论:上⾯的图就⾜矣说明问题,同⼀个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址!

OS将每个区域进行区域划分。用mm_struct结构体保存相关属性。 上述已经提到了每个进程都有地址空间,那么多的进程地址空间,OS需不需要管理呢???需要的,如何管理:先描述,再组织。

1.2 虚拟内存管理

linux下进程的地址空间的所有的信息的结构体是 mm_struct (内存描述符)。每个进程只有⼀个mm_struct结构,在每个进程的task_struct结构中,有⼀个指向该进程的结构。

  • mm_struct保存的部分属性信息如下:

struct mm_struct { // struct vm_area_struct mmap; / 指向虚拟区间(VMA)链表 / struct rb_root mm_rb; / red_black树 / unsigned long task_size; /具有该结构体的进程的虚拟地址空间的⼤⼩/ // // 代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。 unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; /…*/ }

mm_struct结构是对整个⽤⼾空间的描述。每⼀个进程都会有⾃⼰独⽴的mm_struct,这样每⼀个进程都会有⾃⼰独⽴的地址空间才能互不⼲扰。先来看看由task_struct到mm_struct,进程的地址空间的分布情况:

因为上述已经说过进程地址空间存在不同的区域,为了达到快速访问的目的,使用vm_area_struct来;连接各个虚拟内存区域(VMA)。

linux内核使⽤ vm_area_struct 结构来表⽰⼀个独⽴的虚拟内存区域(VMA),由于每个不同质的虚拟内存区域功能和内部机制都不同,因此⼀个进程使⽤多个vm_area_struct结构来分别表⽰不同类型的虚拟内存区域。上⾯提到的两种组织⽅式使⽤的就是vm_area_struct结构来连接各个VMA,⽅便进程快速访问。

  • vm_area_struct内核结构如下:

struct vm_area_struct { unsigned long vm_start; //虚存区起始 unsigned long vm_end; //虚存区结束 struct vm_area_struct *vm_next, *vm_prev; //前后指针 struct rb_node vm_rb; //红⿊树中的位置 unsigned long rb_subtree_gap; struct mm_struct *vm_mm; //所属的 mm_struct pgprot_t vm_page_prot; unsigned long vm_flags; //标志位 struct { struct rb_node rb; unsigned long rb_subtree_last; } shared; struct list_head anon_vma_chain; struct anon_vma *anon_vma; const struct vm_operations_struct *vm_ops; //vma对应的实际操作 unsigned long vm_pgoff; //⽂件映射偏移量 struct file * vm_file; //映射的⽂件 void * vm_private_data; //私有数据 atomic_long_t swap_readahead_info; #ifndef CONFIG_MMU struct vm_region vm_region; / NOMMU mapping region */ #endif #ifdef CONFIG_NUMA struct mempolicy vm_policy; / NUMA policy for the VMA */ #endif struct vm_userfaultfd_ctx vm_userfaultfd_ctx; } __randomize_layout;

将上述结构进行量化,如图:

1.3 为什么存在虚拟地址空间

  • 内存隔离与安全性: 虚拟地址空间为每个进程提供独立的内存视图,避免了进程间直接访问彼此内存的风险。这样,即使一个进程出现问题,它的错误也不会影响到其他进程的运行,确保系统的稳定性与安全性。
  • 简化内存管理: 操作系统通过虚拟地址空间为每个进程提供统一的内存模型,进程无需关心物理内存的具体布局。操作系统可以将虚拟地址映射到不同的物理内存位置,优化内存资源的使用,提高内存管理的灵活性。
  • 支持虚拟内存机制: 虚拟地址空间使得操作系统能够实现虚拟内存机制,将物理内存的使用扩展到磁盘存储。通过分页和分段等技术,操作系统可以将大于物理内存容量的数据载入内存,只在需要时加载部分内容,从而实现程序运行时的内存“扩展”。
  • 进程迁移与共享: 在多任务操作系统中,虚拟地址空间使得进程迁移和共享变得容易。虚拟地址映射机制允许进程在不同的物理机器上运行,或者让多个进程共享某一部分内存(如共享库)。
1.3.1 意义
  • 地址空间隔离与安全性 虚拟地址空间为每个进程提供独立的地址空间,使得不同进程之间的内存互不干扰。这种隔离机制有效防止了进程之间相互访问内存的风险,从而提高了系统的安全性。例如,一个进程不能直接修改另一个进程的内存,避免了内存泄漏或数据破坏的情况。
  • 简化程序开发与管理 对程序员而言,虚拟地址空间让他们无需关心物理内存的分配与管理。程序可以假定自己拥有完整的内存空间,而操作系统通过地址映射将虚拟地址转换为物理地址。这样,开发者可以集中精力在逻辑和功能开发上,减少了复杂的内存管理问题。
  • 内存共享与优化 虚拟地址空间还支持进程间的内存共享。例如,当多个进程需要访问相同的共享库时,操作系统可以通过映射同一块物理内存到各自的虚拟地址空间中,从而避免重复加载相同的内容,节省内存资源。此外,虚拟内存还允许操作系统通过分页技术将内存按需分配,优化内存使用。
  • 支持虚拟内存机制 虚拟地址空间使得虚拟内存的概念成为可能。通过虚拟内存,操作系统能够将物理内存和磁盘空间结合使用,允许程序运行超出实际物理内存大小的应用程序。当物理内存不足时,操作系统会将一些数据暂时移至磁盘(交换区),保证进程继续运行,提升了系统的灵活性和吞吐量。

综上,虚拟地址空间不仅保障了内存的安全、管理简便,还提升了内存利用率和系统性能,是现代操作系统不可或缺的基础设施。

2. 最后

本文主要介绍了进程地址空间的基本概念及其管理方式。进程地址空间是操作系统为每个进程提供的独立内存区域,包括代码段、数据段、堆区和栈区等。通过虚拟内存管理,操作系统实现了进程间内存隔离和保护,确保了系统的安全性与稳定性。文章还阐述了虚拟地址空间的意义,包括内存隔离、简化内存管理、支持虚拟内存机制以及内存共享与优化等。通过这些机制,操作系统能够高效管理内存,提升系统性能和资源利用率,是现代操作系统不可或缺的基础。

路虽远,行则将至;事虽难,做则必成

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
【C#】CsvHelper 使用手册
CsvHelper 是一个用于读写 CSV 文件的.NET库。极其快速,灵活且易于使用。
丹枫无迹
2020/05/21
5.7K1
.NET使用CsvHelper快速读取和写入CSV文件
在日常开发中使用CSV文件进行数据导入和导出、数据交换是非常常见的需求,今天我们来讲讲在.NET中如何使用CsvHelper这个开源库快速实现CSV文件读取和写入。
追逐时光者
2024/06/26
4960
.NET使用CsvHelper快速读取和写入CSV文件
【译】使用 Visual Studio Profiler 进行基准测试
在 Visual Studio 17.13 预览版中,我们发布了更新的 BenchmarkDotNet 诊断器,允许您使用性能分析器中的更多工具来分析基准测试。有了这个变化,可以非常快速地挖掘 CPU 使用情况和内存分配,从而使测量/修改/测量周期快速而高效。
郑子铭
2025/03/10
970
【译】使用 Visual Studio Profiler 进行基准测试
在MVC中使用Json.Net序列化和反序列化Json对象
在.Net的MVC开发中,经常会使用到Json对象,于是,系统提供了JsonResult这个对象,其本质是调用.Net系统自带的Json序列化类JavaScriptSerializer对数据对象进行序列化。但是这个系统自带的Json序列化对象方法没有Json.Net好用,于是打算有些时候用Json.Net替代默认的实现。
深蓝studyzy
2022/06/16
1.3K0
.NET 命令行参数包含应用程序路径吗?
发布于 2018-09-11 13:28 更新于 2018-09-13 03:24
walterlv
2018/09/18
5620
.NET 命令行参数包含应用程序路径吗?
C#中通过Mapster实现轻量级高效对象映射器
当谈到 C# 中的对象关系映射时,许多人会立即想到 AutoMapper。事实上,AutoMapper 是一个出色的对象映射库。
郑子铭
2024/12/30
2300
C#中通过Mapster实现轻量级高效对象映射器
JAVA读取csv文件_java读取csv文件某一列
当读取的是一个简单的csv文件,即文件的列字段中不包含分隔符时,可以使用BufferedReader或者Scanner类去读取
全栈程序员站长
2022/11/16
4K0
【愚公系列】2023年01月 .NET CORE工具案例-SharpConfig配置文件读取库
在计算机科学领域,配置文件(英语:configuration file,台湾作设定档)是一种计算机文件,可以为一些计算机程序配置参数和初始设置。
愚公搬代码
2023/01/16
4060
Java读取csv文件Demo
此代码使用BufferedReader从CSV文件中逐行读取内容,并使用逗号分隔符将每行拆分为字符串数组。然后,它将每行数据存储在List<String[]>中。在示例的main方法中,它遍历并打印了每行数据。
默 语
2024/11/20
1340
Java读取csv文件Demo
携程 Apollo 配置中心传统 .NET 项目集成实践
可能由于 Apollo 配置中心的客户端源码一直处于更新中,导致其相关文档有些跟不上节奏,部分文档写的不规范,很容易给做对接的新手朋友造成误导。
Esofar
2019/08/07
8300
携程 Apollo 配置中心传统 .NET 项目集成实践
.NET Core玩转爬虫系列之借助正则表达式入门篇
模拟登录 -> 模拟发送request请求 -> 取回response数据 -> 提取所需信息并将其进行重新组织 -> 存入DB或文件中 -> 后期处理或展示
AI.NET 极客圈
2019/08/23
1.2K1
.NET Core玩转爬虫系列之借助正则表达式入门篇
.NET Core玩转爬虫系列之借助正则表达式入门篇
模拟登录 -> 模拟发送request请求 -> 取回response数据 -> 提取所需信息并将其进行重新组织 -> 存入DB或文件中 -> 后期处理或展示
Enjoy233
2019/08/29
7560
.NET Core玩转爬虫系列之借助正则表达式入门篇
在 .NET 8/9 中使用 AppUser 进行 JWT 令牌身份验证
JWT 身份验证是保护 API 的标准方法之一。这允许无状态身份验证,因为签名令牌是在客户端和服务器之间传递的。在 .NET 8 中,使用 JWT 令牌的方式得到了改进。将它们与 AppUser 类集成将为您的应用程序提供无缝身份验证。本文介绍了在 .NET 8 Web 应用程序中通过 AppUser 类实现 JWT 令牌身份验证的过程。
郑子铭
2024/12/23
4270
在 .NET 8/9 中使用 AppUser 进行 JWT 令牌身份验证
ASP.NET MVC5+EF6+EasyUI 后台管理系统(58)-DAL层重构
前言:这是对本文系统一次重要的革新,很久就想要重构数据访问层了,数据访问层重复代码太多。主要集中增删该查每个模块都有,所以本次是为封装相同接口方法    如果你想了解怎么重构普通的接口DAL层请查看第二节点    如果你只想了解利用T4链接EF生成代码,可以忽略前两节,之后跳后最后T4模版的使用。   (代码在最后)    补充:   最后必须让初学者理解一个知识点:分部类 partial 关键字,因为我们的重构是围绕分部类而实现,包括接口 partial 关键字指示可在命名空间中定义该类、结构或接口的其他
用户1149182
2018/01/16
2K0
ASP.NET MVC5+EF6+EasyUI 后台管理系统(58)-DAL层重构
Entity Framework 4.1 Code-First 学习笔记
  CodeFirst提供了一种先从代码开始工作,并根据代码直接生成数据库的工作方式。Entity Framework 4.1在你的实体不派生自任何基类、不添加任何特性的时候正常的附加数据库。另外呢,实体的属性也可以添加一些标签,但这些标签不是必须的。下面是一个简单的示例:
拓荒者IT
2019/09/25
1.7K0
IIncrementalGenerator 增量 Source Generator 生成代码应用 将构建时间写入源代码
本文将和大家介绍一个 IIncrementalGenerator 增量 Source Generator 生成代码技术的应用例子,将当前的构建时间写入到代码里面。这个功能可以比较方便实现某些功能的开关,比如说设置某个功能自动在具体应用发布之后过一段时间就失效等功能
林德熙
2023/11/28
2350
在 C# 语言中使用 LINQ 对数据进行筛选和排序
LINQ是“语言集成查询”(Language Integrated Query)的缩写。它使开发人员能够直观且高效地与集合进行交互。它为C#及其他.NET语言带来了类似SQL的查询功能,让从各种数据源(如集合、数据库和XML文件)中筛选、排序以及操作数据变得更加容易。
郑子铭
2025/01/22
2180
在 C# 语言中使用 LINQ 对数据进行筛选和排序
【贪玩巴斯】C/C++文件IO流操作的 seekp和seekg详解「建议收藏」
1.seek 是寻找 寻求的意思 2.tell 是告诉 告知的意思 3.那 p 即put 放和输出的意思,在这里是保存到文件 4.那 g 即get 是获取,读入的意思,在这里是从文件读取
全栈程序员站长
2022/09/05
1.6K0
从0到精通,System.Text.Json进阶技巧曝光,性能提升3倍!
在现代软件开发中,JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,被广泛应用于前后端交互、配置文件管理以及分布式系统间的数据传输。System.Text.Json 是 .NET Core 3.0 及更高版本中引入的高性能 JSON 处理库,提供了序列化和反序列化的功能,并且与 .NET 平台深度集成。
郑子铭
2025/04/24
410
从0到精通,System.Text.Json进阶技巧曝光,性能提升3倍!
开源的.NET媒体文件操作组件TagLib#解析
彭泽0902
2018/01/04
8040
推荐阅读
相关推荐
【C#】CsvHelper 使用手册
更多 >
LV.4
后端开发工程师
目录
  • 进程地址空间的奥秘:让你理解程序如何在计算机中生存 本文将介绍进程地址空间的基本概念与结构,帮助读者理解操作系统如何管理和分配内存。进程地址空间指的是操作系统为每个运行的进程分配的内存区域,包括代码段、数据段、堆区、栈区等。这些区域各自有特定的功能与管理方式。代码段用于存放程序执行指令,数据段用于存放全局变量,堆区用于动态分配内存,而栈区则用于存储函数调用时的局部变量和返回地址。通过了解这些内存区域的作用与分配机制,读者能更好地理解操作系统如何保障多进程运行时的内存安全与隔离性。
  • 一. 程序地址空间
    • 1.1 基本概念
    • 1.2 虚拟内存管理
    • 1.3 为什么存在虚拟地址空间
      • 1.3.1 意义
    • 2. 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档