1. 概念

写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当发出fork(  )系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程。这种行为是非常耗时的,因为它需要:

  • 为子进程的页表分配页面
  • 为子进程的页分配页面
  • 初始化子进程的页表
  • 把父进程的页复制到子进程相应的页中

创建一个地址空间的这种方法涉及许多内存访问,消耗许多CPU周期,并且完全破坏了高速缓存中的内容。在大多数情况下,这样做常常是毫无意义的,因为许多子进程通过装入一个新的程序开始它们的执行,这样就完全丢弃了所继承的地址空间。

现在的Unix内核(包括Linux),采用一种更为有效的方法称之为写时复制(或COW)。这种思想相当简单:父进程和子进程共享页面而不是复制页面。然而,只要页面被共享,它们就不能被修改。无论父进程和子进程何时试图写一个共享的页面,就产生一个错误,这时内核就把这个页复制到一个新的页面中并标记为可写。原来的页面仍然是写保护的:当其它进程试图写入时,内核检查写进程是否是这个页面的唯一属主;如果是,它把这个页面标记为对这个进程是可写的。

2. Linux的fork()使用写时复制

传统的fork()系统调用直接把所有的资源复制给新创建的进程。这种实现过于简单并且效率低下,因为它拷贝的数据或许可以共享(This approach is significantly naïve and inefficient in that it copies much data that might otherwise be shared.)。更糟糕的是,如果新进程打算立即执行一个新的映像,那么所有的拷贝都将前功尽弃。Linux的fork()使用写时拷贝(copy-on-write)页实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候。在页根本不会被写入的情况下---例如,fork()后立即执行exec(),地址空间就无需被复制了。fork()的实际开销就是复制父进程的页表以及给子进程创建一个进程描述符。在一般情况下,进程创建后都为马上运行一个可执行的文件,这种优化,可以避免拷贝大量根本就不会被使用的数据(地址空间里常常包含数十兆的数据)。由于Unix强调进程快速执行的能力,所以这个优化是很重要的。

COW技术初窥:

在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。

那么子进程的物理空间没有代码,怎么去取指令执行exec系统调用呢?

在fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。

在网上看到还有个细节问题就是,fork之后内核会通过将子进程放在队列的前面,以让子进程先执行,以免父进程执行导致写时复制,而后子进程执行exec系统调用,因无意义的复制而造成效率的下降。

COW详述:

现在有一个父进程P1,这是一个主体,那么它是有灵魂也就身体的。现在在其虚拟地址空间(有相应的数据结构表示)上有:正文段,数据段,堆,栈这四个部 分,相应的,内核要为这四个部分分配各自的物理块。即:正文段块,数据段块,堆块,栈块。至于如何分配,这是内核去做的事,在此不详述。

1.      现在P1用fork()函数为进程创建一个子进程P2,

内核:

(1)复制P1的正文段,数据段,堆,栈这四个部分,注意是其内容相同。

(2)为这四个部分分配物理块,P2的:正文段->PI的正文段的物理块,其实就是不为P2分配正文段块,让P2的正文段指向P1的正文段块,数据段->P2自己的数据段块(为其分配对应的块),堆->P2自己的堆块,栈->P2自己的栈块。如下图所示:同左到右大的方向箭头表示复制内容。

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

3.       vfork():这个做法更加火爆,内核连子进程的虚拟地址空间结构也不创建了,直接共享了父进程的虚拟空间,当然了,这种做法就顺水推舟的共享了父进程的物理空间

通过以上的分析,相信大家对进程有个深入的认识,它是怎么一层层体现出自己来的,进程是一个主体,那么它就有灵魂与身体,系统必须为实现它创建相应的实体, 灵魂实体与物理实体。这两者在系统中都有相应的数据结构表示,物理实体更是体现了它的物理意义。

补充一点:Linux COW与exec没有必然联系

参考资料

1. Linux进程管理——fork()和写时复制

fork、vfork、clone相关推荐

  1. (五)进程的生命周期——诞生:fork、vfork、clone、内核线程(待续)

    操作系统:linux 处理器:arm 内核版本:4.x fork.vfork.clone cow _do_fork copy_process 内核线程 自然界中的每一个生命都需要经历出生.成长.死亡, ...

  2. linux内核-系统调用fork、vfork与clone

    前面已经简要地介绍过fork与clone二者的作用于区别.这里先来看一下二者在程序设计接口上的不同: pid_t fork(void); int clone(int (*fn)(void *), vo ...

  3. fork、vfork、wait、waitpid

    fork函数: 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进 ...

  4. Linux API-进程:getpid、getppid、exit、wait、fork、vfork、execl、execv、system、popen

    函数说明 一.getpid--取得进程识别码 二.getppid--取得父进程的进程识别码 三.exit--正常结束进程 四.wait--等待子进程中断或结束 五.fork--建立一个新子进程 六.v ...

  5. 分析进程创建、执行、切换以及可执行文件的加载

    sa18225499 原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/ 一.实验步骤及分析 进程描述 我们通过进程控制块来描述来描述进程,又 ...

  6. UNIX高级环境编程(9)进程控制(Process Control)- fork,vfork,僵尸进程,wait和waitpid...

    本章包含内容有: 创建新进程 程序执行(program execution) 进程终止(process termination) 进程的各种ID 1 进程标识符(Process Identifiers ...

  7. OS / Linux / clone、fork、vfork 与 pthread_create 创建线程有何不同

    进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合,这些资源在Linux中被抽象成各种数据对象:进程控制块.虚存空间.文件系统,文件I/O.信号处理函数.所以创建一个进程的过程就是这些数 ...

  8. 【Linux进程、线程、任务调度】二 fork/vfork与写时拷贝 线程的本质 托孤 进程睡眠和等待队列

    学习交流加(可免费帮忙下载CSDN资源): 个人微信: liu1126137994 学习交流资源分享qq群1(已满): 962535112 学习交流资源分享qq群2(已满): 780902027 学习 ...

  9. fork 与 branch、clone 的区别

    fork 与 branch.clone 的区别 fork branch clone   对于没有使用 git 进行企业级的多人协作的 git 使用者来说,往往认为使用 git 进行代码仓的操作的过程中 ...

最新文章

  1. J2EE项目移植问题二
  2. mac 下终端 操作svn命令 以及出现证书错误的处理方法
  3. P1002 [NOIP2002 普及组] 过河卒(python3实现)
  4. Amazon 首席科学家李沐亲授「深度学习」,2019 AI ProCon震撼来袭!(日程出炉)...
  5. origin 复制与数据转置
  6. 对于os.walk()的认识--python遍历文件执行相应操作
  7. 称重仪表显示ol怎么解决_电脑显示器无信号怎么解决呢?
  8. 2022年C语言教程入门和最新C语言自学教程C语言进阶教程大全
  9. C编程实例-“约瑟夫问题” 解法
  10. Android毕业设计选题依据,毕业设计选题依据、目的意义、
  11. 今年-计划写一本java方面的书籍-初稿正式完成
  12. python winrar密码_python+winrar 指令压缩文件 | 学步园
  13. ubuntu系统键盘鼠标失灵的修复方案
  14. Excel2003和Excel2007对下拉选择和下拉级联选择的操作以及java程序的调用
  15. 美团——“疯狂”的吸血鬼
  16. win10 安装配置 MySQL
  17. 百度前端技术体系——百度EFE(Excellent FrontEnd)技术体系
  18. 上传计算机桌面文件图标不见,关于桌面上图标都不见了这类问题的解决方法
  19. 散论陈寅恪先生《对科学院的答复》
  20. web课程设计网页规划与设计 基于HTML+CSS+JavaScript制作智能停车系统公司网站静态模板

热门文章

  1. linux命令:vim文件操作命令、新建用户,查看用户列表,chown命令
  2. [Z]POJ 计算几何入门题目推荐[转PKKJ]
  3. AjaxToolKit学习笔记 之 ModalPopupExtender
  4. java动态拼接请求_在JavaWeb项目中处理静态文件或动态链接拼接网站地址的最优处理方案...
  5. android fragment界面滑动切换效果,Android App中使用ViewPager+Fragment实现滑动切换效果...
  6. centos6安装mysql并远程连接_Ubantu下MySQL安装、部署和远程连接
  7. php 出错处理,PHP 错误处理机制
  8. java多字段排序,java8 stream多字段排序的实现
  9. 用python做数据分析流程图_使用Pyecharts进行高级数据可视化
  10. python集合类型是一种具体的数据类型_Python3基础语法之集合类型