头图

Linux 写时复制机制原理

列旭松V

在 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 为了加速创建子进程过程与节省内存使用的原因,实现了 写时复制 的机制。

写时复制 的原理大概如下:

  • 创建子进程时,将父进程的 虚拟内存物理内存 映射关系复制到子进程中,并将内存设置为只读(设置为只读是为了当对内存进行写操作时触发 缺页异常)。
  • 当子进程或者父进程对内存数据进行修改时,便会触发 写时复制 机制:将原来的内存页复制一份新的,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写。

写时复制 过程如下图所示:

当创建子进程时,父子进程指向相同的 物理内存,而不是将父进程所占用的 物理内存 复制一份。这样做的好处有两个:

  • 加速创建子进程的速度。
  • 减少进程对物理内存的使用。

如上图所示,当父进程调用 fork 创建子进程时,父进程的 虚拟内存页M 与子进程的 虚拟内存页M 映射到相同的 物理内存页G,并且把父进程与子进程的 虚拟内存页M 都设置为只读(因为设置为只读后,对内存页进行写操作时,将会发生 缺页异常,从而内核可以在缺页异常处理函数中进行物理内存页的复制)。

当子进程对 虚拟内存页M 进行写操作,便会触发 缺页异常(因为已经将 虚拟内存页M 设置为只读)。在缺页异常处理函数中,对 物理内存页G 进行复制一份新的 物理内存页G',并且将子进程的 虚拟内存页M 映射到 物理内存页G',同时将父子进程的 虚拟内存页M 设置为可读写。

总结

本篇文章主要介绍了 Linux 写时复制 的原理,写时复制 是 Linux 创建子进程高效的关键所在,而且还能节省对物理内存使用。我们将在下一篇文章中对 写时复制 的实现进行详细的分析。

我们的公众号

阅读 382

《PHP核心技术与最佳实践》一书作者,PHP源码加密模块php-beast( https://github.com/liexusong/php-beast )作者。

33 声望
5 粉丝
0 条评论
你知道吗?

《PHP核心技术与最佳实践》一书作者,PHP源码加密模块php-beast( https://github.com/liexusong/php-beast )作者。

33 声望
5 粉丝
宣传栏