当前位置: 首页 > news >正文

[Linux]进程地址空间

进程地址空间

该图是虚拟地址:
在这里插入图片描述

#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

变量内容不一样,所以父子进程输出的变量绝对不是同一个变量

但地址值是一样的,说明,该地址绝对不是物理地址!

在Linux地址下,这种地址叫做虚拟地址

我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理

在操作系统中,子进程和父进程的虚拟地址空间在创建时是相同的,但它们的物理内存映射可能不同,具体取决于操作系统的内存管理机制。

1、fork() 创建子进程时的行为

当父进程通过 fork() 创建子进程时,子进程会复制父进程的虚拟地址空间:

  1. 虚拟地址相同:子进程的代码段、数据段、堆栈等逻辑地址布局与父进程完全一致。
  2. 物理内存分离:初始时,父子进程的虚拟地址映射到相同的物理内存页,但这些页会被标记为只读(写时复制的优化)。

假设父进程有一个变量 int x = 10,地址为 0x1000:
子进程的 x 也会看到地址 0x1000,但物理内存此时是共享的。
当父进程或子进程尝试修改 x 时,操作系统会触发写时复制,为修改者分配新的物理页,此时两者的物理内存分离,但虚拟地址仍相同。

2、写时拷贝

  1. 核心思想
    共享而非复制:初始时,多个进程(或对象)共享同一份物理内存数据。
    按需拷贝:只有当某个进程尝试写入共享内存时,系统才会真正复制该内存区域,并为写入者分配独立的物理内存副本。
    目的:避免无意义的物理内存复制,提升性能。
    触发条件:当父进程或子进程尝试写入共享内存页时。
    结果:修改者获得独立的物理页,虚拟地址保持不变。

  2. 典型场景:fork() 创建子进程
    传统方式:fork() 直接复制父进程全部内存到子进程,导致大量内存拷贝(即使数据未被修改)。

COW 优化:
共享阶段:fork() 后,父子进程共享所有内存页,但将内存页标记为只读。
触发拷贝:当任一进程尝试写入共享页时,触发页错误(Page Fault)。
内核介入:操作系统检测到写操作,分配新的物理页,复制原页内容到新页,并修改进程的页表映射。
完成写入:进程继续执行写入操作,但此时操作的是独立的物理页。

在这里插入图片描述

  1. 虚拟地址空间的独立性
    进程隔离:每个进程的虚拟地址空间是操作系统分配的独立逻辑视图,彼此隔离。
    物理内存映射不同:即使虚拟地址相同,实际物理内存可能完全不同(如父子进程修改共享页后)。

创建进程,本质是系统多了一个进程,因此需要管理进程

在这里插入图片描述
同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址

Linux为什么要有地址空间?Linux 进程地址空间的设计是操作系统内存管理的核心机制,其核心目标是为进程提供内存保护、隔离和抽象,同时确保系统稳定性和资源的高效利用。

1、内存保护:杜绝系统级越界问题

虚拟地址空间的隔离性:
每个进程拥有独立的虚拟地址空间,通过页表映射到物理内存。进程只能访问其虚拟地址范围内被明确分配的内存区域。

页表的权限控制:
页表中每个内存页标记了读(R)、写(W)、执行(X)权限。例如:
代码段(.text)标记为 R-X(可读、可执行,不可写),防止恶意代码篡改程序逻辑。
数据段(.data)标记为 RW-(可读、可写,不可执行),防止数据段被当作代码执行(防范缓冲区溢出攻击)。

非法访问示例:
若进程尝试通过野指针写入未分配的地址(如 (int)0xdeadbeef = 42),页表中无对应物理页映射,触发段错误(Segmentation Fault),操作系统直接终止进程。

内核空间的保护:
内核内存(如 0xC0000000 以上的高地址空间)在所有进程的虚拟地址中共享,但用户态进程无权访问。任何用户态程序尝试访问内核地址会触发权限异常,确保内核安全。

2、内存抽象:进程视角的“独占内存”

(1) 一致的虚拟空间布局
所有进程的虚拟地址空间范围相同:
例如,在 32 位系统中,每个进程“认为”自己拥有完整的 0x00000000 到 0xFFFFFFFF 地址空间,包含代码段、数据段、堆、栈等区域。
实际物理内存映射不同:
进程 A 的栈地址 0x7ffffff0000 可能映射到物理页 X,而进程 B 的同栈地址映射到物理页 Y。
这种抽象让程序无需关心物理内存的实际分配,简化开发。

(2) 进程的“独占内存”假象
独立的内存视图:
进程认为自己是系统中唯一运行的实体,所有内存区域(代码、堆、栈)均为自己独占。
实际资源共享:
多个进程的代码段可能通过写时拷贝(COW)共享同一物理内存(如 fork() 后的父子进程)。
动态链接库(如 libc.so)被加载到固定虚拟地址,多个进程共享同一物理内存副本。

3、进程独立性:解耦调度与内存管理

(1) 内存管理与进程调度的分离
虚拟地址空间使物理内存管理透明化:
进程调度器只需关注 CPU 时间片的分配,无需关心进程内存的物理位置。
内存管理器通过页表动态分配物理页,甚至可以换出(Swap Out)不活跃进程的内存到磁盘,而不影响进程的虚拟地址视图。

(2) 灵活的内存分配
按需分配物理内存:
进程申请内存(如 malloc())时,操作系统仅分配虚拟地址,物理内存的分配延迟到实际写入时(通过 COW 或缺页中断)。

示例:
进程调用 malloc(1GB),系统立即分配虚拟地址范围,但物理内存可能仅在进程实际写入时逐步分配。

在这里插入图片描述

相关文章:

  • APK文件结构与逆向工具链深度解析
  • 蓝桥杯 17110抓娃娃
  • 登山第十九梯:实时点云压缩——量变质不变
  • FreeRTOS(9)信号量-计数型信号量
  • 半导体工艺(七)干法刻蚀1.0
  • EaseUS Todo Backup Pro v16.0 数据备份还原软件
  • neo4j中常用cql命令汇总(基础版)
  • VS Code远程Docker开发配置指南——完美速通
  • idea 2023社区版自动生成 serialVersionUID
  • 搜广推校招面经四十六
  • FastDDS中Utils定义的那些数据结构(二)
  • redis增加ip白名单
  • 多数元素——面试经典150题(力扣)
  • 30天学习Java第四天——JVM规范
  • Chrome 扩展开发 API实战:Sessions (六)
  • 使用Python实现ICO文件生成工具
  • TensorFLow深度学习实战(11)——风格迁移详解
  • 电脑突然没有声音的可能原因与应对方法
  • NineData:解锁多云与混合云环境下的智能数据管理
  • 艾尔登复刻Ep1——客户端制作、场景切换、网络控制
  • 夜读丨在雨中撒欢儿
  • 德州一女子做医美突发心脏骤停昏迷一个多月,涉事机构已关门拆招牌
  • 再现“黄肠题凑”与汉代生活,北京大堡台遗址博物馆新馆开馆
  • 中外科研人员合作揭开固态电池短路成因
  • 王毅将出席《关于建立国际调解院的公约》签署仪式
  • 国家发改委:安全是低空经济发展的首要前提,稳妥推进低空旅游、航空运动等发展