Linux到底怎样实现写时拷贝其实很简单

Linux到底怎样实现写时拷贝?其实很简单

背景

在多程序程式设计中,我们都知道fork()会产生一个子程序,而且子程序就是父程序的一个副本。按照传统fork的方式,子程序获得父程序资料空间、堆和栈的副本。这种实现方式实在过于简单,粗暴,效率低下。为什么这么说呢?因为在fork之后,往往紧接着就会跟随exec。exec之前拷贝完全是无意义的,而且会极大的限制建立程序的速度。

所以Linux引入了写时拷贝技术(copy-on-write),简称COW。它是一种可以推迟甚至可以免除拷贝资料的技术。fork时,核心此时并不复制整个程序的地址空间,而是让父程序和子程序共享同一个拷贝。只有在需要写入的时候,资料才会被复制,从而使各个程序拥有各自的拷贝。也就是说在此之前都是以只读的方式访问。这种技术使地址空间上的页的拷贝被推迟到真正需要写入的时候。例如我之前说的情况,fork之后立即呼叫exec,他们就不要拷贝。

现在fork之后的实际开销就是复制程序的页表和子程序建立唯一的程序描述符。这是一种极大的优化,避免了大量的无意义的拷贝。对于Linux这种强调快速切换的操作系统来说,这个优化有着重大的意义。

注意:fork之后核心会通过将子程序放入到执行伫列前面,以让子程序先执行。以避免父程序先写入,产生拷贝,而后子程序执行exec,导致因而无意义的拷贝。

详解

在我之前的【Linux内存管理】中我已经讲到了,每个程序都有独立的程序地址空间。我们现在考虑一种实际情况,有一个父程序PID1。它的虚拟地址空间大致有:程式码段,资料段,堆,栈。核心通过页表为他们映射了虚拟地址到实体地址,为了方便表示,就用实体地址块表示吧。(4G地址空间)。

原始fork()

PID1 通过fork()系统呼叫建立了一个子程序PID2。下图简单的直接的表示,可以看出直接为PID2复制了PID1的地址空间资料。

COW技术

核心只为新生成的子程序建立虚拟空间结构,它们来复制于父程序的虚拟究竟结构,但是不为这些段分配实体内存,它们共享父程序的物理空间,当父子程序中有更改相应段的行为发生时,再为子程序相应的段分配物理空间。

总结

通过上面的分析,相信大家对写时拷贝技术有了一个比较深入的认识了。如果在本文中对这些专业术语不是很清楚,建议去看我之前的文章【Linux内存管理】。

猜你喜欢