您的位置:首页 > 运维架构

Copy-On-Write写时复制

2015-04-23 09:40 316 查看
Copy-On-Write解决的问题

早期Unix系统创建进程的方式存在缺陷:当发出fork()系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程。这种行为是非常耗时的,因为它需要:

  - 为子进程的页表分配页帧

  - 为子进程的页分配页帧

  - 初始化子进程的页表

  - 把父进程的页复制到子进程相应的页中

这种创建地址空间的方法涉及许多内存访问,消耗许多CPU周期,并且完全破坏了高速缓存中的内容。并且由于许多子进程通过exec()函数装入一个新的程序开始执行,这样造成之前继承地址空间的操作变成白费。

Copy-On-Write写时复制

创建子进程时,并不复制整个父进程的地址空间,而是让子进程和父进程共享同一个地址空间,不需要写的时候以只读方式共享

只,在需要写入的时候才进行地址空间的复制。

写时复制是常用的优化手段,内存管理器利用它可以节约内存。写时复制是“延迟计算(lazy evaluation)”的一个例子,可以归类于资源延迟分配。只有在真正需要使用资源时才占用资源, 写时复制通常能减少资源的占用。延迟计算使得只有当绝对需要时才执行一个昂贵的操作。

Copy-On-Write机制

父进程和子进程共享页帧而不是复制页帧。然而,只要页帧被共享,它们就不能被修改,即页帧被保护。无论父进程还是子进程何时试图写一个共享的页帧,就产生一个异常,这时内核才把这个页复制到一个新的页帧中并标记为可写。原来的页帧仍然是写保护的:当其他进程试图写入时,内核检查写进程是否是这个页帧的唯一属主,如果是,就把这个页帧标记为对这个进程是可写的。

当进程为一个包含读/写页面的内存区对象映射了一份写时视图,而并非在映射该视图时创建一份进程私有的拷贝(Hewlett Packard OpenVMS操作系统就是这样做的)时,内存管理器将页面拷贝的动作推迟到页面被写入数据的时候。

所有现代的UNIX系统也都使用了这项技术。如:2个进程正在共享3个页面,每个页面都被标记为写时复制,但是这2个进程都不打算修改页面上的任何数据。如果这2个进程中任何一个线程对一个页面执行了写操作,则产生一个内存管理错误。内存管理器看到,此写操作作用在一个写时复制的页面上,所以,它不是将此错误报告为访问违例,而是在物理内存中分配一个新的读/写页面,并且把原始页面中的内容拷贝到新的页面中。同时也更新一下该进程中对应的页面映射信息,使它指向新的页面位置,然后解除异常,从而使得刚才产生错误的那条指令得以重新执行。这一次,写操作成功了。但是,新拷贝的页面现在对于执行写操作的那个进程来说是私有的,对于其它仍然在共享这一写时复制页面的进程来说,它是不可见的。每个往共享页面中写入数据的进程都将获得它自己的私有拷贝。

Copy-On-Write的应用

在调试器中实现断点支持。

例如:在默认情况下,代码页面在起始时都是只能执行的。然而,如果一个程序员在调试一个程序时设置了一个断点,则调试器必须在代码中加入一条断点指令。它是这样做的:首先将该页面的保护模式改变为PAGE_EXECUTE_READWRITE,然后改变指令流。因为代码页面是所映射的内存区的一部分,所以内存管理器为设置了断点的那个进程创建一份私有拷贝,同时其它进程仍然使用原先未经修改的代码页面。

Linux中对进程复制中内存使用的优化

POSIX 子系统利用写时复制来实现 fork 函数,当一个UNIX 应用程序调用fork 函数来创建另一个进程时,新进程所做的第一件事是调用exec 函数,用一个可执行程序来重新初始化它的地址空间。 在fork中,新进程不是拷贝整个地址空间,而是通过将页面标记为写时复制的方式,与父进程共享这些页面。如果子进程在这些页面中写入数据了,则生成一份进程私有的拷贝。如果没有写操作,则2个进程继续共享页面,不会执行拷贝动作。不管怎么样,内存管理器只拷贝一个进程试图要写入数据的那些页面,而不是整个地址空间。

http://cookies5000.blog.163.com/blog/static/995922052009223112797/

PHP内核中的写时复制

http://www.php-internals.com/book/?p=chapt06/06-06-copy-on-write
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  COW 写时复制 内核