1.先直接上代码,看现象
2.快速理解地址空间
子进程会把父进程的很多内核数据结构全拷贝一份
在子进程对g_val的值做修改时,操作系统发现你要修改的值不仅仅只对应子进程的,于是会在物理内存中重新开辟一段空间,然后把g_val拷贝一份,最后再让子进程做修改
操作系统完成的这份工作,叫做写时拷贝
3.细节问题 -- 理解
那为了保证性,可不可以吧数据在创建子进程的时候,全部给子进程拷贝一份??
因为有大部分数据,子进程并不会去进行修改,修改的只有少部分,若不管改不改,上来就直接全部拷贝一份,这不就是妥妥的浪费空间吗
理论上讲,就算子进程把父进程的数据全部修改一遍,也是刚好 == 全部拷贝
因此消耗的空间永远 <= 全拷贝
写时拷贝就是通过调整拷贝的时间顺序,达到有效节省空间的目的
1)如何理解地址空间
I.什么是区域划分
计算机语言来描述就是:
struct area
{
int start;
int end;
}
用小时候的三八线来描述就是,我有一段区域的start和end,同桌也有一段start和end
struct destop
{
struct area left;
struct area right;
}
struct destop d;
d.left.start = 1;
d.left.start = 50;
d.right.start = 50;
d.right.end = 100;
II.地址空间的理解
1.性
2.虚拟性
2)为什么要有地址空间
1.将无序变为有序,让进程以统一的视角看待物理内存以及自己运行的各个区域
2.进程管理模块和内存管理模块进行解耦
而内存管理模块不需要直接与进程管理模块打交道,它可以根据系统的实际内存情况和需求,地进行内存资源的优化和管理,还能提高内存的利用率和系统性能
3.拦截非法请求
3)进一步理解页表和写时拷贝
一开始默认情况下,在已初始化数据区的页表内的权限是rw的,当创建子进程时,两进程的权限都会变为r,而不能写入,当有一方进行写入操作时,OS就会识别到错误:
1.是不是数据不存在物理内存 --- 缺页中断
2.是不是数据需要写时拷贝 --- 发生写时拷贝
3.如果都不是,才进行异常处理
4.如何理解虚拟地址
反汇编:
pid_t id = fork();
return 的本质就是对id进行写入,发生写时拷贝,这就是为什么id会存在两个返回值,一个是子进程访问到的id,一个是父进程访问到的id