fork()写时复制原理

简介: fork()写时复制原理

fork()系统调用创建一个子进程,是父进程的一个副本,父子进程仅有pid的区别。

子进程拥有与父进程相同的进程虚拟地址空间,但如果在fork()时复制父进程的整个地址空间,虽然实现了创建副本的目的,但这种做法不太聪明,因为直接复制父进程的所有内存页是非常耗费资源的,特别是当父进程占用了大量内存时。

为了解决这个问题,操作系统使用了一种叫做 Copy-On-Write(写时复制) 的技术。

思路:

1.当子进程对地址空间上的数据进行读操作时,没必要重新创建一个副本供子进程来读,直接读父进程的地址可以达到同样的效果

2.当子进程对地址空间上的某一页进行写(修改)操作时,由于逻辑上父子进程拥有独立的地址空间,此时修改的必须是子进程自己的地址空间,此时再分配给子进程一页地址空间,这一页空间才是真正意义上属于子进程自己的

实现

fork() 的实现细节

当父进程调用 fork() 时,操作系统会进行以下操作:

  1. 创建子进程:内核会为子进程分配一个新的进程控制块(Process Control Block,PCB),其中包括子进程的进程 ID、进程状态等信息。
  2. 复制页表:页表是一个数据结构,映射进程的虚拟地址空间到物理内存地址。fork() 时,内核不会复制父进程的所有内存,而是只复制父进程的页表,使子进程的页表指向相同的物理内存页。
  3. 设置内存页为只读:为了实现 Copy-On-Write 机制,内核会将父进程和子进程的内存页标记为只读。这样,任何对这些页的写操作都会触发一个页面保护异常(page fault)。
  4. 共享文件描述符:父进程和子进程共享打开的文件描述符,引用计数会增加。

写时复制(copy on write)的实现细节

  1. 初始状态
  • 当父进程调用 fork() 时,子进程会共享父进程的所有内存页,这些内存页都会被标记为只读。
  1. 触发写保护
  • 当父进程或子进程尝试写入某个内存页时,由于该页是只读的,会触发页面保护异常(page fault)。
  1. 处理写保护异常
  • 操作系统捕获这个异常,并执行以下步骤:
  1. 分配一个新的物理内存页。
  2. 将原来只读内存页的内容复制到新的物理页中。
  3. 更新当前进程的页表,使该虚拟地址指向新的物理页。
  4. 将新的物理页设置为可写。

这样,只有试图写入的内存页会被复制,其他未被修改的内存页依然是共享的和只读的。

示例

假设有一个进程 P,其内存布局如下:

虚拟地址 物理地址 内容
0x1000 0xA000 Data1
0x2000 0xB000 Data2
  1. 调用fork()
  • 创建子进程 C,复制页表并共享内存页。
进程 虚拟地址 物理地址 内容
P 0x1000 0xA000 Data1
P 0x2000 0xB000 Data2
C 0x1000 0xA000 Data1
C 0x2000 0xB000 Data2
  1. 标记为只读
  • 内核将这些内存页标记为只读。
  1. 子进程修改内存页
  • 假设子进程 C 修改 0x1000 地址的内容,触发页面保护异常。
  1. 处理页面保护异常
  • 分配一个新的物理页 0xC000
  • 0xA000 页的内容复制到 0xC000
  • 更新子进程 C 的页表,使 0x1000 虚拟地址指向 0xC000
  • 0xC000 设置为可写。
进程 虚拟地址 物理地址 内容
P 0x1000 0xA000 Data1
P 0x2000 0xB000 Data2
C 0x1000 0xC000 Data1 (Modified)
C 0x2000 0xB000 Data2

优点

  1. 节省内存:未修改的内存页依然共享,只有被修改的页才会被复制,节省了大量内存。
  2. 提高效率:避免在 fork() 调用时立即复制整个地址空间,提高了系统调用的性能。

推荐学习 https://xxetb.xetslk.com/s/p5Ibb

目录
相关文章
|
数据建模 计算机视觉
SiMBA:基于Mamba的跨图像和多元时间序列的预测模型
微软研究者提出了SiMBA,一种融合Mamba与EinFFT的新架构,用于高效处理图像和时间序列。SiMBA解决了Mamba在大型网络中的不稳定性,结合了卷积、Transformer、频谱方法和状态空间模型的优点。在ImageNet 1K上表现优越,达到84.0%的Top-1准确率,并在多变量长期预测中超越SOTA,降低了MSE和MAE。代码开源,适用于复杂任务的高性能建模。[[论文链接]](https//avoid.overfit.cn/post/c21aa5ca480b47198ee3daefdc7254bb)
1934 3
|
NoSQL 编译器 Linux
CodeBlocks-20.03下载安装及中文教程
CodeBlocks强大之处 1、跨平台,windows、linux 、mac都可以用 2、轻量化,远不及VS占用空间 3、完全免费
3130 1
CodeBlocks-20.03下载安装及中文教程
|
11月前
|
监控 网络协议 Java
一些适合性能测试脚本编写和维护的工具
一些适合性能测试脚本编写和维护的工具
412 59
|
Linux 持续交付 Docker
掌握Docker:从入门到实践
Docker 是一个开源容器引擎,允许开发者将应用及其依赖打包成可移植的容器,在任意 Linux 机器上运行。本文从基本概念入手,详细介绍 Docker 的安装、基本操作、镜像构建及 Docker Compose 的使用,并通过实战案例展示如何部署 Web 应用、构建微服务架构及实现 CI/CD。通过学习,你将掌握 Docker 的核心功能,提升应用开发和部署效率。
|
12月前
|
Java 程序员 编译器
返回值不同算方法重载么?为什么?
本文解释了为什么返回值不同不算方法重载,强调了方法重载是基于方法名称和参数列表的,而返回值不是方法重载的考虑因素,因为返回值不影响JVM的方法签名,也不会造成编译时的歧义。
293 1
|
机器学习/深度学习 存储 数据采集
数字化与数智化有什么区别?
数字化(Digitalization)是将信息转换为数字(即计算机可读)格式的过程。数智化(Digital and Intelligent Transformation)是数字智慧化与智慧数字化的融合。
602 1
|
存储 缓存 NoSQL
Redis从入门到精通之底层数据结构简单动态字符串(SDS)详解
SDS是Redis中的一种字符串类型,它是一种二进制安全的字符串,由简单动态字符串(SDS)实现。SDS支持多种数据结构,其中字符串(String)是最常用的一种数据结构之一。SDS的优点在于它可以避免C字符串常见的问题,比如缓冲区溢出和内存泄露等。SDS的常数复杂度获取字符串长度和杜绝缓冲区溢出可以避免使用strlen和strcat函数时的一些问题。同时,SDS的空间预分配和惰性空间释放两种策略可以减少修改字符串的内存重新分配次数。SDS也是二进制安全的,因为它不是以空字符串来判断字符串是否结束,而是以len属性表示的长度来判断字符串是否结束。SDS还兼容部分C字符串函数
903 86
Redis从入门到精通之底层数据结构简单动态字符串(SDS)详解
|
XML JSON 分布式计算
如何设计财务对账系统 —— 从0到1搭建对账中心实战
卡拉云快速搭建企业内部对账系统
11324 3
如何设计财务对账系统 —— 从0到1搭建对账中心实战
|
Kubernetes 安全 Cloud Native
Confidential Containers:云原生机密计算基础设施
后续将基于RunD安全容器打造更完整的机密容器技术,包含支持更多的 HW-TEE 平台,以及构建更完备的机密容器支持能力。
Confidential Containers:云原生机密计算基础设施
|
C++
c++ std::lock与std::scoped_lock底层实现原理源码剖析
std::lock用于同时获取多把锁(两把及两把以上)std::scoped_lock定义于c++ 17标准,是std::lock的RAII封装类以下代码纯来自msvc实现。
314 0
c++ std::lock与std::scoped_lock底层实现原理源码剖析