LINUX系统编程-写时复制

调用fork()时, 内核会复制所有的内存数据结构,复制进程的页表项,然后把父进程的地址空间按页复制到子进程的空间中.糟糕的是,这种按页复制方式是十分耗时的.

在现代UNIX系统如Linux中,采用了写时复制的方式,而不是对父进程空间进行整体复制.

写时复制的前提假设很简单:如果有多个进程要读取他们自己那部分资源的副本,那么复制是不必要的.每个进程只要保存一个指向这个资源的指针就可以了,只要没有一个进程修改自己的"副本",每个进程就好像独占那个资源,从而避免了复制带来的开销.

如果某个进程想要修改自己的那份资源"副本",就会开始复制该资源,并把副本提供给这个进程.复制过程对于对进程而言是透明的,这个进程后面就可以反复修改其持有的副本,而其他进程还是共享原来那份没有修改过的资源.

写时复制的好处:如果进程未修改资源,则都不需要执行复制.一般来说,惰性算法的好处就在于它们会尽量延迟代价高的操作,知道必要时才执行.

在使用虚拟内存的场景下,写时复制是以页为基础执行的.所以,只要进程没有修改其全部地址空间,就不需要复制整个地址空间.在fork()调用后,父进程和子进程都以为自己拥有唯一的地址空间,实际上他们共享父进程的原始页,这些页后面可能又会被其他父进程或子进程共享.

写时复制在内核中的创建很简单,这些页被标记为只读,并对内核页相关的数据结构实现写时复制.如果有进程试图修改某个页,就会产生缺页中断,内核处理缺页中断的处理方式就是对页执行一次透明复制.这时会清空该页的写时复制属性,表示这个页不再被共享.现代计算机体系结构中都存在内存管理单元,提供了硬件级别的写时复制支持,所以实现是很容易的.

vfork()

在实现对页写时复制之前,引入了vfork()系统调用.
vfork()调用成功后,其执行结果和fork()是一样的,除了子进程会立即执行一次exec系统调用,或者调用exit()退出.vfork()系统调用会通过挂起父进程,知道子进程终止或执行新的二进制镜像,从而避免地址空间或页表的拷贝.

在这个过程中,父进程和子进程共享相同的地址空间和页表项,并不使用写时复制.实际上,vfork()调用只完成了一件事:复制内部的内核数据结构.(没有进行页表项的复制) 因此,子进程也就不能修改地址空间中的任何内存.

严格来说,vfork()的所有实现都是有bug的:考虑一下这种情况,如果exec调用失败了,父进程将被一直挂起,直到子进程采取措施或退出.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值