一起读《奔跑吧Linux内核(第2版)卷1:基础架构》之写时复制技术
本书籍的第7章节就进程管理中的基本概念展开讲述。进程是程序执行的一个实例,进程是执行中的程序,即一个程序加载到内存后变成了进程。然后根据最新版的POSIX标准谈及进程创建fork()和execve()函数族,进程终止wait()、waitpid()、kill()、以及exit()函数族。由fork()引申vfork()和clone()两个原语。
一个简单程序的执行,会调用fork()来创建一个新的进程,然后会调用execve()函数来执行这个所谓的新程序。fork()通过写时复制技术复制当前进程的相关信息来创建一个全新的子进程。此时此刻,子进程和父进程在各自的进程地址空间执行,但是共享相同的内容。
早期创建新进程会复制父进程所拥有的所有资源,因此效率很低。每次创建子进程时都要把父进程的进程地址空间中的内容复制到子进程,但是子进程不一定全盘接收,有时甚至完全不用父进程的资源。子进程在调用了execve()函数之后,很可能会脱离父进程的共享资源。由此引出了写时复制技术。
写时复制技术就是父进程在创建子进程时不需要复制进程地址空间的内容到子进程,只需要复制父进程的进程地址空间的页表子进程,这样的话,父子进程就共享了相同的物理内存。
如果父子进程中有某一方需要修改某个物理页面的内容时,会触发写保护的缺页异常,然后才复制共享页面的内容,从而让父子进程都拥有各自的副本。进程地址空间以只读方式共享,当需要写入时才发生复制。写时复制是一种可以推迟甚至避免复制数据的技术,在现代操作系统中有广泛的应用。
发生写时复制之前的父子进程映射到同一个物理页帧地址:
发生写时复制之后的父子进程映射到不同的物理页帧地址:
结合以上图示得知,在采用了写时复制技术的Linux内核中,用fork()函数创建一个新进程的开销变得很小,免去了复制父进程整个进程地址空间中的内容的巨大开销,现在只需要复制父进程页表的一点开销。
阅读小结:根据以上特性,我们可以知道写时复制技术的优缺点。优点:对于一些读多写少的数据,写入时复制的做法就很不错,例如配置、黑名单、物流地址等变化非常少的数据,这是一种无锁的实现。可以帮我们实现程序更高的并发。缺点:数据一致性的问题。这种实现只是保证数据的最终一致性,在添加到拷贝数据而还没进行替换的时候,读到的仍然是旧数据。