进程虚拟地址空间

1.程序地址空间

C,C++内存空间布局

也就是程序的虚拟地址空间布局

2.引出虚拟地址

同一个地址查出来的值却不一样

我不知道这是什么地址,但是我可以肯定这肯定不是物理地址

我们之前在C,C++学到的地址都不是物理地址而是虚拟地址

3.虚拟地址是什么

32位地址,虚拟地址空间范围为0-2^32

进程访问内存时,要先进行虚拟内存到物理内存的映射,找到物理内存,才可以访问内存。

在执行程序时,磁盘把有关程序的数据和代码存到物理内存,在对应到页表

操作系统内进程要访问有关程序的数据和代码,就要到物理内存内找,进程就通过访问虚拟地址空间来实现这一过程

那现在我们来回答上一个问题,为什么地址一样数据却不一样
写时拷贝

  • 虚拟地址完全相同 :父子进程中 gavl 和 gavl 的虚拟地址完全一样
  • 物理内存隔离 :子进程修改后,父进程的值保持不变
  • 写时复制(Copy-on-Write) :fork时父子进程共享物理内存,子进程修改时才分配新的物理页
    我们都知道,进程是独立的 ! ! !
    所以当子进程要改变父子进程共有的值时,如果不进行写时拷贝,也就是开辟新的物理空间给子进程,那么父进程就必然会被子进程影响。

4.什么是虚拟地址空间?

就是操作系统给进程画的饼

操作系统给每个进程都分配了虚拟地址空间

虚拟地址空间是需要被管理的

本质是以内核数据结构struct _mm struct

所以如何理解进程是独立的

进程 = 内核数据结构(task_struct,struct _mm struct *mm,页表)+ 代码和数据

结论

结论一:

定义全局变量,全局有效

初始化的全局变量,未初始化的全局变量这两个数据区域位于虚拟地址空间的 低地址部分

  • 全局变量在程序 编译链接阶段 就被分配了固定的虚拟地址
  • 程序加载时,操作系统通过 页表 将这些虚拟地址映射到物理内存
  • 由于虚拟地址固定,任何函数都能通过该地址访问对应的全局变量

结论二:

字符串常量是在代码区的,代码区的权限只可读可执行,不可写

所以字符串常量是不可修改的。

const 是属于在编译时的错误

如果不加const修改是运行时错误

结论3: 命令行参数和环境变量

属于父进程的地址空间内的数据资源,和代码数据区一样

子进程会继承父进程的地址空间。

所以,子进程也能看到命令行参数和环境变量!!!

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

1.保证进程的独立性

有了虚拟地址空间,即使两个不同的进程指向同样的虚拟地址,也会通过页表机制,让他们的物理内存隔离不会影响彼此

有了虚拟地址,当进行到虚拟地址与物理内存转换时会进行安全审核

2."无序"到"有序"

创建进程时先由内核数据结构在加载代码和数据,这是因为惰性加载,

在运行进程时一定要全部加载代码和数据吗?

并不是,是执行边加载

传统加载

  • 进程创建时,将所有代码和数据从磁盘一次性加载到物理内存
  • 缺点:浪费内存(很多代码可能永远不会执行)、启动慢

惰性加载

  • 进程创建时, 只加载必要的内核数据结构 和 极小部分代码
  • 实际执行时, 用到什么就加载什么
  • 优点:节省内存、进程启动快、支持更大的虚拟地址空间

写时拷贝拷贝也是一种惰性加载,只有在子进程影响父进程时才会开辟新物理内存,否则指向同一空间

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

有了虚拟地址空间,这样只有在需要申请空间时才进行内存管理,如果没有那么在进程刚进行时就会申请空间高度耦合

在早期没有虚拟内存的系统中,进程创建时需要立即分配 所有 所需的物理内存空间。例如,如果一个程序需要1GB内存,系统必须立即从物理内存中划出1GB连续空间分配给它,即使程序实际上只使用了其中的100MB。

eg:

cpp 复制代码
// 示例:全局数组预分配
char large_buffer[1024 * 1024 * 1024]; // 预分配1GB全局数组

int main() {
    // 实际只使用前100MB
    memset(large_buffer, 0, 1024 * 1024 * 100);
    return 0;
}

这种方式导致:

  • 高度耦合 :进程创建与物理内存分配直接绑定
  • 资源浪费 :大量物理内存被闲置占用
  • 启动缓慢 :进程需要等待所有内存分配完成才能运行

有了虚拟地址空间的情况

  • 进程创建时,操作系统只为其分配一个 逻辑上的虚拟地址空间 (如4GB或更大)大饼

  • 这个空间只是一个地址范围的承诺,并不立即对应物理内存

  • 进程看到的是连续的虚拟地址,但实际物理内存可以是离散的

  • 只有当进程 实际访问 某个虚拟地址时,操作系统才会:

    • 检查该虚拟地址是否已映射到物理内存
    • 如果没有映射(缺页异常),才从物理内存中分配相应的页
    • 更新页表,建立虚拟地址到物理地址的映射

所以我们可已看出当我们malloc时是在物理内存中开辟空间吗?
并不是,而是在虚拟内存中,当进程需要时才进行内存申请。

补充

详细的虚拟地址空间

使用链表结构高效管理空闲和已分配的内存块

相关推荐
且从容.2 小时前
ubuntu安装flink
linux·ubuntu·flink
爱和冰阔落3 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨3 小时前
WSL2的安装与使用
linux·wsl
微小的鱼3 小时前
Linux的渲染与显示系统链路
android·java·linux
lisanmengmeng4 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
Android系统攻城狮4 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
Linux-lucky16 小时前
45-Linux学习之旅之防火墙与WEB加固
linux·前端·网络·学习·ubuntu
禁默16 小时前
启元 Q1 二次开发实践:Ubuntu 部署与仿真验证,从 PrimeConsole 到 ROS 2 Humble + RViz
linux·运维·ubuntu
skywalk816317 小时前
把 去也网站 从 Ubuntu 搬到 FreeBSD:一次 Service Jail + nginx 的部署实录
linux·运维·nginx·ubuntu·freebsd
爱莉希雅&&&19 小时前
GitLab 完整学习笔记
linux·运维·服务器