在 Linux 系统中,调用 fork
系统调用创建子进程时,并不会把父进程所有占用的内存页复制一份,而是与父进程共用相同的内存页,而当子进程或者父进程对内存页进行修改时才会进行复制 —— 这就是著名的 写时复制
机制。
下面我们将分析 Linux 写时复制(Copy On Write)
机制的原理。
虚拟内存与物理内存
进程的内存可分为 虚拟内存
和 物理内存
。
物理内存
:就是电脑安装的内存条,如果电脑安装了2GB的内存条,那么系统就用于 0 ~ 2GB 的物理内存空间。虚拟内存
:虚拟内存是使用软件虚拟的,在 32 位操作系统中,每个进程都独占 4GB 的虚拟内存空间。
应用程序使用的是 虚拟内存
,比如 C 语言取地址操作符号 &
所得到的地址就是 虚拟内存地址
。而 虚拟内存地址
需要映射到 物理内存地址
才能使用,如果使用没有映射的 虚拟内存地址
,将会导致 缺页异常
。
虚拟内存地址
映射到 物理内存地址
如下图所示:
如上图所示,进程A与进程B的相同 虚拟内存地址
映射到不同的 物理内存地址
,这就是不同进程的相同虚拟内存地址互不影响的原因。
写时复制原理
前面介绍了 虚拟内存
与 物理内存
的概念,接下来将会介绍 Linux 写时复制
的原理。
前面说过,虚拟内存
需要与 物理内存
进行映射才能使用,如果不同进程的 虚拟内存地址
映射到相同的 物理内存地址
,那么就实现了共享内存的机制。如下图所示:
由于进程A的 虚拟内存M
与进程B的 虚拟内存M'
映射到相同的 物理内存G
,所以当修改进程A 虚拟内存M
的数据时,进程B 虚拟内存M'
的数据也会跟着改变。
Linux 为了加速创建子进程过程与节省内存使用的原因,实现了 写时复制
的机制。
写时复制
的原理大概如下:
创建子进程时,将父进程的
虚拟内存
与物理内存
映射关系复制到子进程中,并将内存设置为只读(为什么要设置为只读?)。当子进程或者父进程对内存数据进行修改时,便会触发
写时复制
机制:将原来的内存页复制一份新的,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写。
写时复制
过程如下图所示:
从上图可知,当创建子进程时,父子进程指向相同的 物理内存
,而不是将父进程所占用的 物理内存
复制一份。这样做的好处有两个:
加速创建子进程的速度。
减少进程对物理内存的使用。
但这个时候只能对内存进行读操作,如果父进程或子进程对内存进行写操作,那么将会触发 缺页异常
,而在 缺页异常
处理中会对物理内存进行复制,并且重新映射其内存映射关系。
复制并重新映射到新的物理内存后,父子进程的虚拟内存就映射到不同的物理内存上,这时父子进程都可以对内存进行写操作而互不影响,所以需要把父子进程的内存读写权限设置为可读写。
总结
本篇文章主要介绍了 Linux 写时复制
的原理,写时复制
是 Linux 创建子进程高效的关键所在,而且还能节省对物理内存使用。我们将在下一篇文章中对 写时复制
的实现进行详细的分析。
- END -
看完一键三连在看,转发,点赞
是对文章最大的赞赏,极客重生感谢你
推荐阅读