COW奶牛!Copy On Write机制了解一下

只有光头才能变强

在读《Redis设计与实现》关于哈希表扩容的时候,发现这么一段话:

执行BGSAVE命令或者BGREWRITEAOF命令的过程中,Redis需要创建当前服务器进程的子进程,而大多数操作系统都采用写时复制(copy-on-write)来优化子进程的使用效率,所以在子进程存在期间,服务器会提高负载因子的阈值,从而避免在子进程存在期间进行哈希表扩展操作,避免不必要的内存写入操作,最大限度地节约内存。

触及到知识的盲区了,于是就去搜了一下copy-on-write写时复制这个技术究竟是怎么样的。发现涉及的东西蛮多的,也挺难读懂的。于是就写下这篇笔记来记录一下我学习copy-on-write的过程。

本文力求简单讲清copy-on-write这个知识点,希望大家看完能有所收获。

一、Linux下的copy-on-write

在说明Linux下的copy-on-write机制前,我们首先要知道两个函数:fork()和exec()。需要注意的是exec()并不是一个特定的函数, 它是一组函数的统称, 它包括了execl()、execlp()、execv()、execle()、execve()、execvp()。

1.1简单来用用fork

首先我们来看一下fork()函数是什么鬼:

fork is an operation whereby a process creates a copy of itself.

fork是类Unix操作系统上创建进程的主要方法。fork用于创建子进程(等同于当前进程的副本)。

新的进程要通过老的进程复制自身得到,这就是fork!

如果接触过Linux,我们会知道Linux下init进程是所有进程的爹(相当于Java中的Object对象)

Linux的进程都通过init进程或init的子进程fork(vfork)出来的。

下面以例子说明一下fork吧:

#include <unistd.h>   #include <stdio.h>     int main ()    {        pid_t fpid; //fpid表示fork函数返回的值       int count=0; // 调用fork,创建出子进程       fpid=fork(); // 所以下面的代码有两个进程执行!     if (fpid < 0)            printf("创建进程失败!/n");        else if (fpid == 0) {           printf("我是子进程,由父进程fork出来/n");            count++;       }       else {           printf("我是父进程/n");            count++;       }       printf("统计结果是: %d/n",count);       return 0;   }  

得到的结果输出为:

我是子进程,由父进程fork出来 统计结果是: 1 我是父进程 统计结果是: 1

解释一下:

fork作为一个函数被调用。这个函数会有两次返回,将子进程的PID返回给父进程,0返回给子进程。(如果小于0,则说明创建子进程失败)。

再次说明:当前进程调用fork(),会创建一个跟当前进程完全相同的子进程(除了pid),所以子进程同样是会执行fork()之后的代码。

所以说:

父进程在执行if代码块的时候,fpid变量的值是子进程的pid

子进程在执行if代码块的时候,fpid变量的值是0

1.2再来看看exec()函数

从上面我们已经知道了fork会创建一个子进程。子进程的是父进程的副本

exec函数的作用就是:装载一个新的程序(可执行映像)覆盖当前进程内存空间中的映像,从而执行不同的任务

exec系列函数在执行时会直接替换掉当前进程的地址空间

我去画张图来理解一下:

exec函数的作用

参考资料:

程序员必备知识——fork和exec函数详解https://blog.csdn.net/bad_good_man/article/details/49364947

linux中fork()函数详解(原创!!实例讲解):https://blog.csdn.net/jason314/article/details/5640969

linux c语言 fork() 和 exec 函数的简介和用法:https://blog.csdn.net/nvd11/article/details/8856278

Linux下Fork与Exec使用:https://www.cnblogs.com/hicjiajia/archive/2011/01/20/1940154.html

Linux 系统调用 —— fork()内核源码剖析:https://blog.csdn.net/chen892704067/article/details/76596225

1.3回头来看Linux下的COW是怎么一回事

fork()会产生一个和父进程完全相同的子进程(除了pid)

如果按传统的做法,会直接将父进程的数据拷贝到子进程中,拷贝完之后,父进程和子进程之间的数据段和堆栈是相互独立的

父进程的数据拷贝到子进程中

但是,以我们的使用经验来说:往往子进程都会执行exec()来做自己想要实现的功能。

所以,如果按照上面的做法的话,创建子进程时复制过去的数据是没用的(因为子进程执行exec(),原有的数据会被清空)

既然很多时候复制给子进程的数据是无效的,于是就有了Copy On Write这项技术了,原理也很简单:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygygg.html