问题
进程在 fork 创建子进程时几乎拷贝了父进程的所有数据,这可能导致进程的创建效率十分低效;除此之外,子进程通常会加载新的程序覆盖拷贝的数据段、堆栈等,这又使得先前复制的数据全部浪费掉;就算没有执行exec函数,从父进程拷贝的大量内存数据对子进程来讲可能没有任何意义。
解决方案
作为替代,系统内核在fork时使用了写时复制(Copy-On-Write)技术,写时复制的主要作用就是将复制推迟到写操作真正发生时,这也就避免了大量无意义的复制操作。现在的多数操作系统中,fork 并不会立刻对父进程的内存空间进行复制,而是利用共享同一物理内存。
操作过程
在 fork 函数调用时,父进程和子进程会被内核分配不同的虚拟内存空间,所以从进程的角度看它们访问的是不同的内存:
- 在虚拟内存空间进行读操作时,内核会将虚拟内存映射到物理内存上,父子进程共享了物理上的内存空间;
- 当父进程对共享的内存进行修改时,共享的内存会以页为单位进行拷贝,父进程会对原有的物理空间进行修改,而子进程会使用拷贝后的新物理空间;
- 当子进程对共享的内存进行修改时,子进程会在拷贝后的新物理空间上进行修改,而不会改动原有的物理空间,避免影响父进程的内存空间;
写时复制减少了不必要的物理内存的开销,使得进程的创建速度非常快,提供了一种很好的共享内存使用思想。在一些内存型数据库、大数据分析等应用场景下都具有借鉴意义。
特点
如果父子进程频繁进行写操作,可能会产生大量的分页错误(页异常中断page-fault),这样就得不偿失。因此在使用写时复制操作时,要特别注意需求是写多读少还是写少读多,选择合适的应用场景。