COW写时复制

Linux 下的 COW

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

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

这也是进程和线程的区别之一,线程共享内存,而进程内存独立。

但很多时候复制给子进程的数据是用不着的,浪费性能和时间。

于是就有了 Copy On Write 这项技术,原理也很简单:

  • fork 创建出的子进程,与父进程共享内存空间。子进程的地址空间指向父进程。但对这些内存空间只有读权限。

  • 子进程写内存时,CPU硬件检测到内存页是只读的,于是触发页异常中断(page-fault),陷入 kernel 的一个中断例程。中断例程中,kernel 就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。

好处

  • 减少分配和复制大量资源时带来的瞬间延时

  • 减少不必要的资源分配。比如 fork 进程时,并不是所有的页面都需要复制,父进程的代码段和只读数据段都不被允许修改,所以无需复制

缺点

  • 如果在 fork() 之后,父子进程都还需要继续进行写操作,那么会产生大量的分页错误 (页异常中断page-fault),这样就得不偿失。

Redis 的 COW

Redis 在执行 RDB 持久化时,如果是采用 BGSAVE/ BGREWRITEAOF 的方式,那 Redisfork 出一个子进程来读取数据,这个子进程采用的便是 COW ,子进程的地址空间指向父进程,这样就可以间接读到父进程数据。而不是复制一份数据到内存里,再持久化这份复制的数据。

文件系统的COW

对数据进行修改的时候,不会直接在原来的数据位置上进行操作,而是重新找个位置修改。这样的好处是一旦系统突然断电,重启之后不需要做 Fsck (file system check)。好处就是能保证数据的完整性,掉电的话容易恢复。

比如说:要修改数据块A的内容,先把A读出来,写到B块里面去。如果这时候断电了,原来A的内容是还在的。

Last updated