✅<1>主页::我的代码爱吃辣
📃<2>知识讲解:Linux——程序地址空间
☂️<3>开发环境:Centos7
💬<4>前言:我们一直随口就能说出来的栈区,堆区,常量区,到底是什么?今天学习一下进程地址空间。
目录

我们在学习C语言的阶段,大家一定见过这样的空间布局图:

我们仅仅只是知道有这个东西, 可是我们并不了解程序地址空间。
测试代码:
- #include
- #include
- #include
- int g_val = 0;
- int main()
- {
- pid_t id = fork();
- if (id < 0)
- {
- perror("fork");
- return 0;
- }
- else if (id == 0) // child
- {
- printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
- }
- else // parent
- {
- printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
- }
- sleep(1);
- return 0;
- }
输出结果:

我们发现,输出出来的变量值和地址是一模一样的,很好理解呀,因为子进程按照父进程为模版,父子并没有对变量进行进行任何修改。
我们尝试让子进程,修改变量:
测试代码:
- #include
- #include
- #include
- int g_val = 0;
- int main()
- {
- pid_t id = fork();
- if (id < 0)
- {
- perror("fork");
- return 0;
- }
- else if (id == 0) // child
- {
- g_val = 1000;
- printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
- }
- else // parent
- {
- printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
- }
- sleep(1);
- return 0;
- }
输出结果:

我们发现,父子进程,输出地址是一致的,但是变量内容不一样!!能得出如下结论:
上面说到用C/C++语言所看到的地址,全部都是虚拟地址或者说叫做线性地址,那么虚拟地址和物理地址有什么关系?
引入地址空间:
每一个进程都有自己的地址空间,进程地址空间地址实际上就是虚拟地址。我们通过堆虚拟空间位置上的数据修改,最终经过转换到物理内存上的数据修改。
虚拟地址是如何与物理地址进行映射的呢?
因为有页表的存在。
页表是一种K/V映射表,连接虚拟内存与物理内存,每一个进程都会有自己的进程地址空间,那么每一个进程地址空间都会有与之对应的页表。

注意:
如何解释上面同一个地址却又不同的值的现象:
创建子进程的本质,就是创建了子进程PCB和加载对应的代码和数据。子进程和父进程代码共享,数据在修改时会发生写是拷贝。

注意:
上面的图就足矣说名问题,同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址!
如果没有进程地址空间,直接映射到物理地址不是更好吗,省去这么多中间的转换。效率不是更高吗?
如果没有进程地址空间,OS直接将映射到物理地址,如果有那一次我们地址映射错了,就有可能映射到其他地址空间上,这样进程之间独立性就会被破坏。
使用了地址空间,就不会出现进程错误的访问其他进程的地址空间吗?是的,即使我们出现了错误地址错误的地址也是一个虚拟地址,仍然在该进程的地址空间内,而地址空间是每一个进程独立的,映射到物理地址也是独立的。
综上所述:
我们在使用malloc和new的时候,我们都知道他们的功能都是在堆上开辟空间,然后返回开辟的空间的首地址。
如果我们开辟了空间我们不去使用,那么这个空间会属于我们吗?
注意: