引入
在学习C语言的时候,内存包括栈区、堆区、静态区
![](https://img-blog.csdnimg.cn/img_convert/ed65699a80e61d688b08d466c801de07.png)
这个布局是内存吗? 不是!! 这是进程地址空间!
下面测试一下:
![](https://img-blog.csdnimg.cn/img_convert/67b7090af4d8a94a51f9a89a46729770.png)
![](https://img-blog.csdnimg.cn/img_convert/35858f51168a30ed21f3cf394d3c773f.png)
11540是bash进程
我们修改一下源程序,在观察下结果
![](https://img-blog.csdnimg.cn/img_convert/373554a8a0f4f394325656f19a381eaa.png)
![](https://img-blog.csdnimg.cn/img_convert/a6b8bb210d24ab5f09348a7c4b8a82d8.png)
发现父进程的g_value的值不变,这里得出一个结论:
子进程对全局数据修改,并不影响父进程
进程具有独立性 进程 = 内核数据结构+代码和数据
保证进程的独立性的方法是 写时拷贝
这里还有一个问题,就是我们可以发现子进程和父进程的地址是一样的,那么地址相同为什么变量的结果不同呢?
如果假设是物理地址,结果会不同吗? 答案肯定是不可能读取同一变量的地址,读到不同的数值!
所以这里的地址绝对不是物理地址
我们在语言层面用的地址,不是物理地址
我们称这种地址叫做 虚拟地址or线性地址
我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理
OS必须负责将虚拟地址转化成物理地址 。
进程地址空间
故事1
![](https://img-blog.csdnimg.cn/img_convert/dcde9a58c05323271779b7b4e122384d.png)
故事2
小花VS小胖,两个小学生,闹了矛盾,最后在桌子上化了个线,画线的本质:区域划分!
区域划分:对线性区域进行1指定的start和end即可完成区域划分
![](https://img-blog.csdnimg.cn/img_convert/bc16d755a8d6345b234ae8cb40de3faf.png)
![](https://img-blog.csdnimg.cn/img_convert/d9c54cca451bb756ab157475e3e63618.png)
两人相安无事了一阵子,过了一段时间,双方中的一方通过暴力扩张,已经占有整个桌面的70%甚至80%,该种行为我们称为扩大区域 ,修改start\end值即可。
![](https://img-blog.csdnimg.cn/img_convert/94359dd35ae9b13da0d3347cba29c518.png)
所以之前说‘程序的地址空间‘是不准确的,准确的应该说成进程地址空间 ,那该如何理解呢?看图
![](https://img-blog.csdnimg.cn/img_convert/a58fa654ca2cbb66e37899d00e4026ea.png)
上面的图就足矣说名问题,同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了 不同的物理地址!
fork在返回的时候,父子都有了,return两次,返回的本质就是写入、谁先返回,谁就让OS发生写实拷贝
扩展1 如果没有进程地址空间,os如何工作
如果没有进程地址空间,代码和数据存储在物理内存,那么会造成一些问题,如果寻址的地址出现越界,会影响到其他程序,无法保证进程的独立性。
![](https://img-blog.csdnimg.cn/img_convert/323b6768e220ca40be0ee653c4aaa87b.png)
为什么要有进程地址空间?
1 防止地址随意访问,保护物理内存与其他进程
2 将进程管理和内存管理进行解耦合
3 可以让进程以统一的视角,看待自己的代码和数据
扩展2 malloc本质
申请空间成功但是暂时不用,闲置状态,也会造成浪费
malloc先申请一个空间,假设我们在堆区上申请一个空间,即将堆区扩大,往上申请,然后将虚拟地址填入页表,但是物理地址暂时不填,同时也不在物理地址中申请空间,调用malloc就直接返回。采用缺页中断的方式
![](https://img-blog.csdnimg.cn/img_convert/b7632edb6b550df79255abd5eea0a515.png)