Linux---进程(八)程序地址空间(虚拟地址空间)

1、进程虚拟空间的布局

如下图可以看清楚代码段在低地址;只读数据段、数据段、BSS段依次向上;堆区从低地址向高地址增长;栈区从高向低地址增长;命令行参数和环境变量位于栈的最顶端。

2、虚拟地址引入

虚拟地址是进程看到的"逻辑地址",操作系统通过页表和写时复制,让相同的虚拟地址对应不同的物理内存,从而实现了进程间的地址隔离和高效内存管理。

如下图:

从输出可以清晰看到:

  • 子进程: child[30501]: 100 : 0x601058
  • 父进程: parent[30500]: 0 : 0x601058

这里的核心现象是:

  • 变量值不同:子进程里 g_val 是 100 ,父进程里 g_val 还是 0 。

  • 变量地址相同:两者的变量地址都是 0x601058 。

通俗解释

  • 地址相同:这个地址是虚拟地址,每个进程都有自己独立的虚拟地址空间,所以父子进程里同一个变量的虚拟地址可以完全一样。

  • 值不同:子进程修改 g_val 时,操作系统触发了写时复制(COW),给子进程单独分配了一块物理内存,把修改后的值存在了新的物理页上。所以,虽然虚拟地址相同,但它们实际对应的物理内存已经是两块不同的区域,因此值互不影响。

虚拟地址总结

虚拟地址是进程自己看到的"门牌",操作系统通过页表把相同的"门牌"映射到了不同的"房间"(物理内存),所以值不一样。

  1. 刚 fork() 完
  • 父子进程共用同一块物理内存

  • 虚拟地址一样,物理地址也一样

  1. 子进程一修改数据
  • 操作系统发现:有人要改共享内存

-立刻给子进程新开辟一块物理内存
- 把原来的数据复制过去
- 只修改子进程的页表,让它的虚拟地址指向这块新物理内存

  1. 结果
  • 子进程改的是新物理内存

  • 父进程还是用老物理内存

  • 所以:子进程怎么改,都完全不影响父进程

最简总结:

虚拟地址没变 → 页表变了 → 物理内存换了

这就是:写时复制(COW)

虚拟地址空间概念

虚拟地址空间是OS给进程画的一张饼

OS对虚拟地址空间的管理

系统中有很多的进程,每个进程有自己的虚拟地址空间,所以OS要管理这些虚拟地址空间,

操作系统给每个进程"伪造"了一整片连续的地址空间,

这就叫虚拟地址空间。

每个进程的虚拟地址空间,

都由一个叫 struct mm_struct 的结构体来管理。

操作系统通过管理所有进程的 mm_struct ,

实现对所有虚拟地址空间的统一管理。

操作系统负责管理所有进程的虚拟地址空间。

  • 每个用户进程:

→ 有一个 struct mm_struct

  • 操作系统:

→ 通过管理一堆 mm_struct 来管理所有虚拟地址空间

如何理解虚拟地址空间中的区域划分?

我用最简单、最考试、最能记住的方式,只讲核心,你一下就懂:

虚拟地址空间的区域划分 ------ 一句话理解

就是把进程那块"假的虚拟地址",按用途分成好几块,每块干不同的事,互不干扰。

为什么要划分?

  • 代码不能随便改

  • 数据可以读写

  • 函数调用要用栈

  • malloc 要用堆

如果不分区,全部混在一起,程序一跑就崩溃。

所以操作系统把虚拟地址空间分成不同区域,这就叫:区域划分

分成哪几个区?(只记最核心 4 个)

  1. 代码区

放程序代码,只读,不能改。

  1. 数据区

放全局变量、静态变量。

  1. 堆区(heap)

malloc 动态申请内存用,从下往上长。

  1. 栈区(stack)

局部变量、函数调用用,从上往下长。

总结:

虚拟地址空间是假的地址范围,

操作系统为了安全、好管理,

把它按功能分成不同区域,

这就叫 虚拟地址空间的区域划分。

1、什么是页表?

页表是操作系统维护的地址映射表,负责将进程的虚拟地址翻译成物理内存的真实地址,每个进程有独立的页表。

2. 查表工作谁来做?

主要由 CPU 内的 MMU(内存管理单元) 硬件完成。

发生页缺失时,由操作系统内核介入处理。

3. 为什么要有虚拟地址空间?

隔离保护:进程间互不干扰,提升系统稳定性。

简化编程:程序员无需关心物理内存细节。

高效利用:通过分页、共享、交换等技术,突破物理内存限制。

地址规整:提供连续的虚拟地址块,方便内存管理。

或:

  1. 控制进程的行为(拦截进程的非法行为),保护物理内存。

  2. 有了虚拟地址空间和页表,原则上:将进程的内存空间布局,无序变有序。

  3. 让进程管理和内存管理,解耦合

懒加载

在 Linux 进程创建和内存管理的语境下,懒加载(Lazy Loading / 延迟加载) 指的是:

进程创建时,操作系统不立即把代码和数据全部加载到物理内存,而是先创建好内核数据结构(如 task_struct 、 mm_struct 、页表等),建立好虚拟地址空间的映射关系。

当进程真正执行到某段代码、访问某个数据时,发现对应的页不在物理内存里,触发页错误(Page Fault),操作系统才会把那一页的内容从磁盘加载到内存,并更新页表。

相关推荐
cyber_两只龙宝1 小时前
Tomcat--企业级web应用服务器详细介绍与整合Nginx配置流程
linux·运维·前端·nginx·云原生·tomcat·负载均衡
A.A呐1 小时前
【Linux第十章】进程控制
linux
我是Superman丶2 小时前
Nginx反向代理流式输出延迟?一招解决SSE/WebSocket缓冲问题SpringBoot+SSE流式输出卡住?Nginx这个配置必须关!
运维·websocket·nginx
开开心心就好2 小时前
内存清理软件灵活设置,自动阈值快捷键清
运维·服务器·windows·pdf·harmonyos·risc-v·1024程序员节
waves浪游2 小时前
库制作与原理(上)
linux·运维·服务器·开发语言·c++
wefg12 小时前
【Linux】进程地址空间的内核空间
linux·运维·服务器
dustcell.2 小时前
高性能web服务器
android·服务器·前端
不知名。。。。。。。。2 小时前
Linux网络基础
运维·服务器·网络
2023自学中2 小时前
Linux 内核文件 rest_init 函数:流程与总结
linux·uboot