进程虚拟地址空间

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 小时前
linux mmap 底层实现
linux·linux内核
刘某某.2 小时前
linux 常用命令学习
linux·运维·学习
“αβ”2 小时前
传输层协议--TCP协议
linux·服务器·网络·网络协议·tcp/ip·http·https
明洞日记2 小时前
【软考每日一练007】位图计算与内存管理深度全解
c++·算法·ai·操作系统·进程
万叶学编程2 小时前
Navicat连接Linux主机(MySQL)失败
linux·运维·服务器
Coder个人博客2 小时前
Linux6.19-ARM64 crypto NH-Poly1305 NEON子模块深入分析
linux·网络·算法·车载系统·系统架构·系统安全·鸿蒙系统
weixin_462446232 小时前
Python 实战:将 HTML 表格一键导出为 Excel(xlsx)
linux·python·excel·pandas
济6173 小时前
linux 系统移植(第九期)----Linux 内核顶层 Makefile- make xxx_defconfig 过程-- Ubuntu20.04
linux·运维·服务器
宴之敖者、3 小时前
Linux——yum和vim
linux·运维·服务器