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时是在物理内存中开辟空间吗?
并不是,而是在虚拟内存中,当进程需要时才进行内存申请。
补充
详细的虚拟地址空间

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