fork创建子进程详解

一.前言

在上一篇文章-进程的概念,最后我们提到了创建进程的方式有两种方式,一种是手动的创建出进程,还有一种就是我们今天要学习的使用代码的方式创建出子进程-fork

而学习fork创建出进程的过程中,我们会遇到以下问题,待会我们会一一的解决掉。

I fork干了什么?
II 为什么要创建出子进程?
III 为什么fork的两个返回值,给父进程返回子进程的pid,给子进程返回0
IV fork之后,父子进程谁先运行
V 为什么fork会有两个返回值
VI 同一个变量为什么会有两个值

二.fork干了什么?

fork是一个系统调用函数,我们可以使用fork,也就是代码的方式创建出一个进程出来。

image-20240706152023582

这里文档说的很清楚,fork可以创建出一个子进程出来,是不是呢?我们验证一下便知。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{printf("这是一个父进程,pid:%d,ppid:%d\n",getpid(),getppid());fork();//不带返回值printf("这是一个进程,pid:%d,ppid:%d\n",getpid(),getppid());    sleep(1);return 0;
}

image-20240706155033372

在fork之前,打印了父进程这一行,在fork之后,打印了两行,可以看出第三行的ppid是第一行和第二行的pid。

由此验证成功:fork之前父进程执行代码,fork之后,创建出子进程,和父进程一起执行后续的代码。

三.为什么要创建出子进程?

我们创建出子进程具体是为了干嘛呢?显然是为了让子进程完成和父进程不一样的工作,执行不一样的代码。

那怎么保证父子进程可以执行不一样的代码呢?

上述我们可以看出fork是有返回值的,通过返回值我们判断谁是子,谁是父,然后让它们执行不同的代码片段。

比如说需要执行边下载边播放的任务,这可能是单进程完成不了的,这时候就需要我们使用fork创建出子进程,然后通过返回值,将代码进行分流,父进程执行播放任务,子进程执行下载任务。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{printf("这是一个父进程,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//父进程的运行{while (1){printf("这是一个父进程,我正在进行播放任务,pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);}}sleep(1);return 0;
}              

image-20240707094515692

fork创建出子进程,系统中就会多一个进程,子进程会以父进程为模版,为子进程创建出PCB,子进程PCB的大部分信息和父进程PCB中的内容是一致的,其中少部分不一样,比如进程的pid和ppid等。

但是现在创建出来的子进程是没有代码和数据的,那子进程的代码和数据从哪里来呢?目前子进程和父进程共享代码和数据!

image-20240707100403518

所以fork之后,子进程会和父进程执行一样的代码。

那fork之前呢?子进程可以看见fork之前的代码吗?

答案是子进程可以看见父进程之前的代码,那子进程为什么没有执行fork之前的代码呢?

因为eip寄存器保存了代码的执行逻辑,当执行到fork之后,eip指向fork位置,eip也会被子进程继承。

所以这就是为什么子进程不执行fork之前的代码,而执行fork之后的代码。

四.为什么fork的两个返回值,给父进程返回子进程的pid,给子进程返回0

image-20240707100652855

在我们现实生活中,一个父亲可以有很多子女,而一个子女只能有一个父亲。而在进程中也是一样的,父进程:子进程=1:n,作为父进程,如何将子进程给跟踪和管理好呢?父进程就需要知道子进程的pid,因为进程的pid具有唯一性。

而子进程通过返回0,知道自己是子进程,从而执行特定于子进程的逻辑。

五.fork之后,父子进程谁先运行

在上面我们可以看到,fork之后,都是父进程先运行,然后子进程再运行。每次都是遵行这种逻辑吗?

当创建出子进程之后,这只是一个开始,接下来是父进程,子进程和系统中的其他进程等待cpu调度执行。当父子进程的pcb都被创建并在运行队列中排队的时候,哪一个进程先被调度,哪一个进程就先运行!

其中谁先被调度,这是不确定,这是由各自PCB中的调度算法(时间片,优先级)+调度器算法等共同决定的。

六.为什么fork会有两个返回值

对于一个函数来说,代码逻辑执行到return了,它的核心工作做完了吗?答案是做完了。

fork函数中大致的做了哪些工作呢?

image-20240707102424669

fork之后,代码共享,那return是代码吗?这是肯定的,return是代码,那么return也要被共享。

父进程被调度的时候,要执行return。子进程被调度的时候,也要执行return。所以说这就是为什么一个fork会有两个返回值。

真实情况是操作系统通过一些寄存器做到返回值返回两次。

七.同一个变量为什么有两个值

刚刚说到,为什么fork会有两个返回值,但是fork只有一个变量,一个变量为什么会有两个值啊?

当我们启动QQ和微信的时候,我们将QQ进程杀死时,会影响微信的运行吗?显然是不会的,所以说进程之前是具有独立性的。

那么父子进程之间是否具体独立性呢?

杀死父进程,子进程一样在运行

image-20240707105608456

杀死子进程,父进程一样在运行

image-20240707105911306

所以说不管是父子进程,还是什么进程,只要是不同的两个进程,他们都是互相独立的,各自有各自的PCB

进程=可执行程序+PCB,其中pcb是各自私有的,可执行程序中包含代码和数据,代码本身就是可读的,也没谁去修改代码,

但是数据父子进程是可能会修改的。

比如,父进程中有个全局变量,当全局变量是0的时候,父进程就退出了,而子进程会修改全局变量为0,这样父进程不就因为子进程的修改,而导致退出了吗?这样还怎么保证进程的独立性呢?

所以说,数据各个进程必须各自私有一份!这里用到的技术是写时拷贝!

写时拷贝(Copy-on-Write,简称 COW)是一种计算机程序设计中的优化策略。

当多个进程或线程共享一块数据时,一开始它们共享相同的物理内存。只有当其中一个进程或线程尝试修改这块数据时,才会真正为其创建一份独立的副本进行修改,而其他未修改的进程或线程仍然共享原来的数据。

这种策略的好处是可以减少不必要的数据复制操作,节省内存和系统资源,提高程序的性能和效率。特别是在涉及到大量数据的共享和少量修改的情况下,写时拷贝能显著优化性能。

写时拷贝就相当于深拷贝和浅拷贝的结合!

image-20240707112135787

这里的id是父进程定义的变量,这个变量是数据,返回的时候,因为父子进程的返回id是不同的,各自写入了id值,发生了写时拷贝,所以一个变量会有不同的值。

按道理说,不同的值,他们的地址肯定不同才对,我们验证一下:

image-20240707113051077

image-20240707113013625

太奇怪了,同一个变量,他们是不同的值,但是他们的地址却是一样的,现在我们只能知道:该地址绝对不是物理地址!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/867773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ECharts在最新版本中使用getInstanceByDom报错处理

引用问题导致报错 如果按如下引用的话&#xff0c;会报错 import echarts from “echarts/lib/echarts”; 原因 在 ECharts 的之前版本中&#xff0c;默认导出了一个名为 echarts 的对象&#xff0c;所以使用 import echarts from “echarts” 是没有问题的。但是在 ECharts …

【Spring Boot】关系映射开发(二):一对多映射

关系映射开发&#xff08;二&#xff09;&#xff1a;一对多映射 1.编写实体1.1 新建 School 实体1.2 新建 Teacher 实体 2.测试映射关系 单向关系的一对多注解 oneToMany&#xff0c;只用于关系的发出端&#xff08;一 的一方&#xff09;。另外&#xff0c;需要关系的发出端定…

android之蓝牙遥控器新增键值

文章目录 简述连接蓝牙代码流程总结简述 使用android 10平台来适配蓝牙遥控器新增的键值 连接蓝牙 当使用遥控器与蓝牙进行配对成功后,就可以通过getevent获取蓝牙打印的信息,如下所示 其中000700a0是发送过来的协议(0007)和码值(00a0)的组合。0xfa是驱动定义好的值,如果…

【LabVIEW学习篇 - 4】:程序结构——条件结构、事件结构、禁用结构

文章目录 条件结构案例一&#xff08;布尔输入&#xff09;案例二&#xff08;整数输入&#xff09;案例三&#xff08;字符串输入&#xff09; 事件结构案例一案例二 禁用结构 条件结构 条件结构的组成部分&#xff1a; 选择器标签&#xff08;带方框的“?”&#xff09;&…

机械硬盘坏了怎么导出数据?5中高效恢复数据的方法

面对机械硬盘损坏的紧急情况&#xff0c;如何有效地导出数据成为了许多用户关注的焦点。以下是对上述方法的深入分析与润色&#xff0c;旨在为用户提供更加全面、清晰的指导。 机械硬盘损坏后的数据导出策略 1. 利用数据恢复软件&#xff1a; 当机械硬盘出现逻辑故障或轻微物…

中标麒麟 RAC 19c 部署(Openssh免密BUG解决方案)

部署环境&#xff1a; 主机一主机二host ip192.168.80.46192.168.80.47vip 192.168.80.48192.168.80.49private ip192.168.10.10192.168.10.11storage ip192.168.20.33192.168.20.34主机名rac19c1rac19c2 需要上传的软件包&#xff1a; 一.虚拟机配置 选择中标麒麟IOS文件&am…

如何在忘记密码的情况下解锁Android手机?

您的 Android 设备密码有助于保护您的数据并防止您的个人信息被滥用。但是&#xff0c;如果您被锁定在Android设备之外怎么办&#xff1f;我们知道忘记您的 Android 手机密码是多么令人沮丧&#xff0c;因为它会导致您的设备和数据无法访问。在本技术指南中&#xff0c;我们将向…

java 闭锁(CountDownLatch)

闭锁&#xff08;CountDownLatch&#xff09;是Java中的一个同步辅助类&#xff0c;用于协调多个线程之间的协作。它允许一个或多个线程等待&#xff0c;直到在其他线程中执行的一组操作完成。闭锁非常适用于需要等待一组事件发生之后再执行某些操作的场景。 import java.uti…

JVM相关知识点汇总

JDK,JRE以及JVM的关系 我们的编译器到底干了什么事? 仅仅是将我们的 .java 文件转换成了 .class 文件,实际上就是文件格式的转换,对等信息转换。 类加载机制是什么? > **所谓类加载机制就是** > ``` > 虚拟机把Class文件加载到内存 > 并对数据进行校验,转换…

LeetCode 744, 49, 207

目录 744. 寻找比目标字母大的最小字母题目链接标签思路代码 49. 字母异位词分组题目链接标签思路代码 207. 课程表题目链接标签思路代码 744. 寻找比目标字母大的最小字母 题目链接 744. 寻找比目标字母大的最小字母 标签 数组 二分查找 思路 本题比 基础二分查找 难的一…

WordPress网站添加插件和主题时潜在危险分析

WordPress 最初只是一个简单的博客软件&#xff0c;现在据估计为全球前 1000 万个网站中的 30% 提供支持。WordPress受欢迎的因素之一是可以轻松创建插件和主题来扩展它并提供比默认设置更多的功能。 目前&#xff0c;WordPress 网站列出了 56,000 多个插件以及数千个主题。插件…

《梦醒蝶飞:释放Excel函数与公式的力量》9.3.1PV 函数

9.3.1 函数简介 PV函数用于计算一系列未来付款的现值&#xff0c;考虑了一定的利率。现值是未来金额的贴现值&#xff0c;表示在当前时刻相当于未来某一时间点的总价值。 9.3.2 语法 PV函数的语法如下&#xff1a; PV(rate, nper, pmt, [fv], [type]) rate&#xff1a;每期…

数字化精益生产系统--QMS质量管理系统

QMS质量管理系统&#xff08;Quality Management System&#xff09;是现代企业管理的关键组成部分&#xff0c;旨在确保产品和服务的质量达到或超过客户需求和期望。 以下是对QMS质量管理系统的功能设计&#xff1a;

ReAct Agent 分享回顾

在人工智能的迅速发展中&#xff0c;ReAct Agent作为一项前沿技术&#xff0c;受到越来越多的关注。本文结合ReAct Agent 提出者的访谈内容&#xff0c;探讨ReAct Agent的研究背景、技术挑战、未来展望&#xff0c;以及它与大模型的紧密联系&#xff0c;分析其科研成果与商业化…

树莓派5安装冬瓜HAOS教程

原文来自瀚思彼岸和hasshome 一、安装前准备 &#xff08;1&#xff09;软件 1、树莓派烧录软件Imager 2、冬瓜HAOS镜像 &#xff08;2&#xff09;硬件 1、树莓派5 2、TF卡&#xff08;SanDisk Extreme PRO 64GB U3 A2 V30 4k&#xff09; 3、读卡器 4、键盘和鼠标 5、显…

Vue3+.NET6前后端分离式管理后台实战(二十九)

1&#xff0c;Vue3.NET6前后端分离式管理后台实战(二十九)

2.5 C#视觉程序开发实例1----IO_Manager实现脉冲输出控制

2.5 C#视觉程序开发实例1----IO_Manager实现脉冲输出控制 1 目标效果视频 目标效果展示 IO_Manager 2 信号输出流程说明 为了防止线程不同步导致输出信号没有被输出&#xff0c; 尽量使用一个输出队列来进行输出的管理 3 IO_Manager中添加内容 3.0 添加两个类 1 Out_Sta…

VSCode推荐插件:Copy Class Name快速复制html中的类名

插件地址&#xff1a;https://marketplace.visualstudio.com/items?itemNamemouday.copy-class-name 复制Vue和React中HTML代码的类名&#xff0c;实现快速复制 使用方式&#xff1a; 选中代码&#xff0c;右键复制类名&#xff0c;再粘贴到文件中即可 示例 <div clas…

Flink SQL kafka连接器

版本说明 Flink和kafka的版本号有一定的匹配关系&#xff0c;操作成功的版本&#xff1a; Flink1.17.1kafka_2.12-3.3.1 添加kafka连接器依赖 将flink-sql-connector-kafka-1.17.1.jar上传到flink的lib目录下 下载flink-sql-connector-kafka连接器jar包 https://mvnreposi…

最新整理的机器人相关数据合集(1993-2022年不等 具体看数据类型)

机器人安装数据是指记录全球或特定区域内工业机器人新安装数量的信息&#xff0c;这一数据由国际机器人联合会(IFR)等权威机构定期发布。这些数据不仅揭示了机器人技术的市场需求趋势&#xff0c;还反映了各国和地区自动化水平及产业升级的步伐。例如&#xff0c;数据显示中国在…