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

目录
相关文章
|
2月前
|
Linux
linux内核执行fork时对写时复制的设置
linux内核执行fork时对写时复制的设置
|
4月前
|
NoSQL Linux Redis
c++开发redis module问题之避免在fork后子进程中发生死锁,如何解决
c++开发redis module问题之避免在fork后子进程中发生死锁,如何解决
|
5月前
|
算法 Linux 调度
Linux进程——进程的创建(fork的原理)
Linux进程——进程的创建(fork的原理)
179 2
|
6月前
|
算法 Linux Shell
【linux进程(二)】如何创建子进程?--fork函数深度剖析
【linux进程(二)】如何创建子进程?--fork函数深度剖析
|
6月前
|
算法 Unix Linux
进程原理及系统调用
进程原理及系统调用
|
算法 Linux 调度
进程原理及其系统调用(上)
进程原理及其系统调用
128 0
|
Unix Linux
进程原理及其系统调用(下)
进程原理及其系统调用
79 0
C++中fork函数的使用及原理
C++中fork函数的使用及原理,在C++中,fork函数用于创建一个新的进程称为子进程,该进程与原始进程几乎完全相同。 fork函数的基本概况 fork() 函数调用成功之后,会有两个返回值。当前进程,也就是父进程返回子进程的 pid,子进程返回 0。如果函数调用错误,返回为-1。 #include <stdio.h> #include <unistd.h> int main(void) { int i = 0; printf("i\tson/pa\tppid\tpid\tfpid\n"); for (i = 0; i < 2; i++) { p
309 0
|
Shell Linux C语言
【Linux进程】三、进程控制——fork()系统调用深度刨析
【Linux进程】三、进程控制——fork()系统调用深度刨析
285 0
【Linux进程】三、进程控制——fork()系统调用深度刨析
|
PHP C++
phpCOW机制(写时复制)
phpCOW机制(写时复制)
84 0
phpCOW机制(写时复制)