【Linux】fork函数详解and写时拷贝再理解

2024-04-28 03:12

本文主要是介绍【Linux】fork函数详解and写时拷贝再理解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤
📃个人主页 :阿然成长日记 👈点击可跳转
📆 个人专栏: 🔹数据结构与算法🔹C语言进阶🔹C++🔹Liunx
🚩 不能则学,不知则问,耻于问人,决无长进
🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍

文章目录

  • 一、fork是什么?
  • 二、fork使用演示
  • 三、问题解决
    • 1.fork为什么fork()要给子进程返回0,给父进程返回子进程pid?
    • 2. fork()函数创建子进程
    • 3.读时共享,写时拷贝
  • 四、写时拷贝的原理

一、fork是什么?

fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。

总结来说就是:
1️⃣ 创建一个子进程,其返回值为pid_t。
2️⃣ 调用fork函数之后,一定是两个进程同时执行fork函数之后的代码,而之前的代码以及由父进程执行完毕。
3️⃣ fork成功后产生两个进程,一个是子进程,一个是父进程。
(1)子进程fork函数返回0,
(2)父进程fork返回新创建子进程的进程PID,
(3)错误返回负值。
4️⃣ 4.fork是把已有的进程复制一份,当然把PCB也复制了一份,然后申请一个PID:子进程的PID=父进程的PID+1;
5️⃣fork后的代码会执行两遍。
6️⃣读时共享,写时拷贝

二、fork使用演示

运行如下代码:

  1 #include<stdio.h>2 #include<unistd.h>3 4 int main(void)                                                                                                                               5 {6 7   fork();8   printf("after: only one line\n");9   sleep(1);10   return 0;11 }

我们只写了一个printf语句,却打印了两遍。也印证了5️⃣fork后的代码会执行两遍。原因就是:fork执行后生成父子进程。
在这里插入图片描述
那么根据第三点3️⃣ fork成功后产生两个进程,一个是子进程,一个是父进程。子进程fork函数返回0;父进程fork返回新创建子进程的进程PID。我们在写一段代码来验证一下。

#include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(void){printf("begin: 我是一个进程, pid: %d, ppid: %d\n", getpid(), getppid());pid_t id = fork();if(id == 0){// 子进程while(1){printf("我是一个子进程, pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);}}else if(id > 1){// 父进程while(1){printf("我是一个父进程, pid: %d, ppid: %d\n", getpid(), getppid());          sleep(1);}}else{printf("error, fork创建子进程失败\n");}return 0;
}

在这里插入图片描述
看到这里你会惊奇的发现,这个代码的if…else…分支可以同时进去。这在以前是没有发生过的。这也正是父子进程同时执行的结果。

三、问题解决

1.fork为什么fork()要给子进程返回0,给父进程返回子进程pid?

  • 上面我们说到当进程的代码执行到fork()函数的时候,会将执行流一分为二,父子进程通过不同的 id 返回值来区分,以此执行不同的代码块。:因为父子进程是两个不同的进程,所以需要根据这个不同的返回值来进程区别
  • 再就是为什么一定是给父进程返回子进程的PID呢?不能反过来吗?这是因为,父亲只能有一个,但是可以有很多儿子,如果给父进程返回0,那么有那么儿子的标识都是0,就无法区分了。

2. fork()函数创建子进程

  • 在上面我们讲到过【进程 = 内核数据结构 + 代码+数据 】,当我们在执行完fork()函数后,子进程被创建出来,那么它的PCB结构体即 task_struct 会被构建出来,我们知道的是在每个进程的结构体中有PID和PPID这两个成员,而且对于子进程中的PPID恰好就是父进程中的PID +1。所以子进程大部分的属性就是以父进程为模版创建的,相当于把父进程拷贝了一份,对部分属性做了修改。
    在这里插入图片描述

也就是说,子进程几乎拷贝了父进程的所有内容,包括代码数据和绝大部分的PCB。上面我们谈到子进程要去拷贝数据的原因是在于【并发修改】的问题,但若是子进程只是读取数据但是不修改呢?也需要去完整地拷贝一份数据吗?不,完全不需要!这会使得资源消耗过大!(如上图🖕)

3.读时共享,写时拷贝

fork函数执行时,不会直接给子进程拷贝一份父进程的数据,在子进程刚被创建的时候,代码和数据全部都是被共享的,只有当操作系统识别到子进程要对父进程中的数据做修改时,才会在系统的某一个位置开辟一段空间,然后在修改的时候不去修改父进程内部的这个数据,而是去修改拷贝出来的这块数据此为父子进程之间数据层面的写时拷贝。
在这里插入图片描述

四、写时拷贝的原理

1.为什么要使用写时拷贝?

其核心思想是,如果有多个调用者同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这个过程对其他的调用者是透明的(transparently)。此作法的主要优点是如果调用者没有修改该资源,就不会有副本(private copy)被建立,因此多个调用者只是读取操作是可以共享同一份资源。

  • 上面的官方术语简单来说就是: 写时拷贝是浅拷贝解决浅拷贝析构冲突的一种解决方案,几个对象共用一块空间,当执行读操作时不会有影响,当你需要进行写操作改变一个对象的内容时,空间的值不能被修改,会互相影响,那么就需要单独开辟一块空间将对象拷贝过去然后改,不改变就不需要开辟。

2.写时拷贝的原理

  • 写时拷贝技术实际上是运用了一个 “引用计数” 的概念来实现的。

  • 在开辟的空间中多维护四个字节来存储引用计数
    有两种方法:
    ①:多开辟四个字节(pCount)的空间,用来记录有多少个指针指向这片空间。
    ②:在开辟空间的头部预留四个字节的空间来记录有多少个指针指向这片空间。【常用】

  • 当我们多开辟一份空间时,让引用计数+1,如果有释放空间,那就让计数-1,但是此时不是真正的释放,是假释放,等到引用计数变为 0 时,才会真正的释放空间。

  • 如果有修改或写的操作,那么也让原空间的引用计数-1,并且真正开辟新的空间。

这篇关于【Linux】fork函数详解and写时拷贝再理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/942195

相关文章

SQL BETWEEN 语句的基本用法详解

《SQLBETWEEN语句的基本用法详解》SQLBETWEEN语句是一个用于在SQL查询中指定查询条件的重要工具,它允许用户指定一个范围,用于筛选符合特定条件的记录,本文将详细介绍BETWEEN语... 目录概述BETWEEN 语句的基本用法BETWEEN 语句的示例示例 1:查询年龄在 20 到 30 岁

基于Linux的ffmpeg python的关键帧抽取

《基于Linux的ffmpegpython的关键帧抽取》本文主要介绍了基于Linux的ffmpegpython的关键帧抽取,实现以按帧或时间间隔抽取关键帧,文中通过示例代码介绍的非常详细,对大家的学... 目录1.FFmpeg的环境配置1) 创建一个虚拟环境envjavascript2) ffmpeg-py

CSS place-items: center解析与用法详解

《CSSplace-items:center解析与用法详解》place-items:center;是一个强大的CSS简写属性,用于同时控制网格(Grid)和弹性盒(Flexbox)... place-items: center; 是一个强大的 css 简写属性,用于同时控制 网格(Grid) 和 弹性盒(F

spring中的ImportSelector接口示例详解

《spring中的ImportSelector接口示例详解》Spring的ImportSelector接口用于动态选择配置类,实现条件化和模块化配置,关键方法selectImports根据注解信息返回... 目录一、核心作用二、关键方法三、扩展功能四、使用示例五、工作原理六、应用场景七、自定义实现Impor

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

一文详解MySQL如何设置自动备份任务

《一文详解MySQL如何设置自动备份任务》设置自动备份任务可以确保你的数据库定期备份,防止数据丢失,下面我们就来详细介绍一下如何使用Bash脚本和Cron任务在Linux系统上设置MySQL数据库的自... 目录1. 编写备份脚本1.1 创建并编辑备份脚本1.2 给予脚本执行权限2. 设置 Cron 任务2

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

HTML5 搜索框Search Box详解

《HTML5搜索框SearchBox详解》HTML5的搜索框是一个强大的工具,能够有效提升用户体验,通过结合自动补全功能和适当的样式,可以创建出既美观又实用的搜索界面,这篇文章给大家介绍HTML5... html5 搜索框(Search Box)详解搜索框是一个用于输入查询内容的控件,通常用于网站或应用程