欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > [Linux]进程地址空间

[Linux]进程地址空间

2025/3/14 0:07:26 来源:https://blog.csdn.net/weixin_51304981/article/details/146204889  浏览:    关键词:[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{ //parentsleep(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),系统立即分配虚拟地址范围,但物理内存可能仅在进程实际写入时逐步分配。

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词