fork创建子进程详解

news2024/11/27 4:15:08

一.前言

在上一篇文章-进程的概念,最后我们提到了创建进程的方式有两种方式,一种是手动的创建出进程,还有一种就是我们今天要学习的使用代码的方式创建出子进程-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.coloradmin.cn/o/1903491.html

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

相关文章

[数据结构] 基于选择的排序 选择排序堆排序

标题&#xff1a;[数据结构] 基于选择的排序 选择排序&&堆排序 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 &#xff08;一&#xff09;选择排序 实现&#xff1a;(默认从小到大排序) 优化后实现方法&#xff1a; &#xff08;二&#xff09;堆排序…

【帧中继实验-ensp】

实验要求 在R1上开启一个点对点子接口&#xff0c;用于连接 R1–R2&#xff0c;两端IP地址为12.1.1.x 。开启一个多点子接口 &#xff0c;用于连接R1–R3&#xff0c;R4&#xff0c;两段IP地址为134.1.1.x。 具体DLCI分配和映射关系如下&#xff1a; R1 102 R2 201—动态映射…

微软正在放弃React

最近&#xff0c;微软Edge团队撰写了一篇文章&#xff0c;介绍了微软团队如何努力提升Edge浏览器的性能。但在文中&#xff0c;微软对React提出了批评&#xff0c;并宣布他们将不再在Edge浏览器的开发中使用React。 我将详细解析他们的整篇文章内容&#xff0c;探讨这一决定对…

QML:Settings介绍

用途 提供持久的独立于平台的应用程序设置。 用户通常希望应用程序在会话中记住其设置&#xff08;窗口大小、位置、选项等&#xff09;。Settings能够以最小的工作量保存和恢复此类应用程序设置。 通过在Settings元素中声明属性来指定各个设置值。仅支持由QSettings识别的值…

Gemma2——Google 新开源大型语言模型完整应用指南

0.引言 Gemma 2以前代产品为基础&#xff0c;提供增强的性能和效率&#xff0c;以及一系列创新功能&#xff0c;使其在研究和实际应用中都具有特别的吸引力。Gemma 2 的与众不同之处在于&#xff0c;它能够提供与更大的专有模型相当的性能&#xff0c;但其软件包专为更广泛的可…

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

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

JavaEE初阶-网络原理1

文章目录 前言一、UDP报头二、UDP校验和2.1 CRC2.2 md5 前言 学习一个网络协议&#xff0c;最主要就是学习的报文格式&#xff0c;对于UDP来说&#xff0c;应用层数据到达UDP之后&#xff0c;会给应用层数据报前面加上UDP报头。 UDP数据报UDP包头载荷 一、UDP报头 如上图UDP的…

中英双语介绍美国苹果公司(Apple Inc.)

中文版 苹果公司简介 苹果公司&#xff08;Apple Inc.&#xff09;是一家美国跨国科技公司&#xff0c;总部位于加利福尼亚州库比蒂诺。作为全球最有影响力的科技公司之一&#xff0c;苹果以其创新的产品和设计引领了多个科技领域的变革。以下是对苹果公司发展历史、主要产品…

算法刷题笔记 滑动窗口(C++实现,非常详细)

文章目录 题目描述基本思路实现代码 题目描述 给定一个大小为n ≤ 10^6的数组。有一个大小为k的滑动窗口&#xff0c;它从数组的最左边移动到最右边。你只能在窗口中看到k个数字。每次滑动窗口向右移动一个位置。以下是一个例子&#xff1a; 该数组为 [1 3 -1 -3 5 3 6 7]&…

SAP PS学习笔记02 - 网络,活动,PS文本,PS文书(凭证),里程碑

上一章讲了PS 的概要&#xff0c;以及创建Project&#xff0c;创建WBS。 SAP PS学习笔记01 - PS概述&#xff0c;创建Project和WBS-CSDN博客 本章继续讲PS的后续内容。包括下面的概念和基本操作&#xff0c;以及一些Customize&#xff1a; - 网络&#xff08;Network&#xf…

CC工具箱使用指南:【相交占比分析】

一、简介 需求场景如下&#xff0c;有【待分析地块】和【面积占比参考】2个图层。2个图层之间存在空间上的重叠。工具的目的是为了分析出【待分析地块】的每1个图斑中&#xff0c;和【面积占比参考】相交的面积&#xff0c;以及和总面积的占比。 举一个应用场景为例&#xff0…

java信号量(Semaphore)

Java中的信号量&#xff08;Semaphore&#xff09;是一种用于控制多个线程对共享资源的访问的同步工具。它可以用来限制可以同时访问某些资源的线程数量。Semaphore 提供了一个计数器来管理许可证的获取和释放&#xff0c;每个许可证代表对资源的一次访问权限。 import java…

阶段三:项目开发---搭建项目前后端系统基础架构:任务11:搭建项目后台系统基础架构

任务描述 1、了解搭建民航后端框架 2、使用IDEA创建基于SpringBoot、MyBatis、MySQL、Redis的Java项目 3、以原项目为参照搭建项目所涉及到的各个业务和底层服务 4、以原项目为例&#xff0c;具体介绍各个目录情况并参照创建相关文件夹 任务指导 1、讲框架的选择和原理 …

解决Unable to Correct Problems ‘You have Held Broken Packages’

进入 Software & Updates 后下拉 Download from&#xff0c;点击 Other… 点击 Select Best Server 等待测试服务器 测试完成后会默认标红测试出的最好的那个服务器&#xff0c;直接点击 Choose Server&#xff0c;可能需要输入系统用户密码5. 输入然后返回上级界面 点击 C…

实现ubuntu的任务计划反弹shell

1.实验目的 使用Ubuntu定时任务反弹shell 2实验环境 ubuntu&#xff1a;ip地址&#xff1a;192.168.80.133 kali&#xff1a;ip地址&#xff1a;192.168.80.134 3.编写crontab计划任务 在ubuntu的系统中使用crontab -e命令编写计划任务 作用&#xff1a;是将一个交互式的…

STM32利用FreeRTOS实现4个led灯同时以不同的频率闪烁

在没有接触到FreeRTOS时&#xff0c;也没有想过同时叫两个或两个以上的led灯闪烁的想法&#xff0c;接触后&#xff0c;发现如果想叫两个灯同时以不同的频率闪烁&#xff0c;不能说是不可能&#xff0c;就算是做到了也要非常的麻烦。但是学习了FreeRTOS后&#xff0c;发现要想同…

26 华三防火墙安全区域

防火墙区域规划 配置网络网卡的地址在同一网段 第一个问题 为什么防火墙直连在同一个网段ping不通? 配置IP地址 local区域: 将local区域的所有接口启用 华三防火墙的local区域是指设备本地接口所在的区域&#xff0c;也称为局域网&#xff08;LAN&#xff09;或内部网络 Int…

机器学习与深度学习:区别(含工作站硬件推荐)

一、机器学习与深度学习区别 机器学习&#xff08;ML&#xff1a;Machine Learning&#xff09;与深度学习&#xff08;DL&#xff1a;Deep Learning&#xff09;是人工智能&#xff08;AI&#xff09;领域内两个重要但不同的技术。它们在定义、数据依赖性以及硬件依赖性等方面…

硬件开发笔记(二十四):贴片电容的类别、封装介绍,AD21导入贴片电容、原理图和封装库3D模型

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140241817 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

VUE3初学入门-02-VUE创建项目

创建VUE项目的另一个方法 三种方法通过vue-cli进行创建通过npm进行创建比较 部署到nginx修改配置生成部署文件 三种方法 上一篇是在VSCODE中建立工作区&#xff0c;然后创建&#xff0c;属于命令加鼠标方式。个人感觉&#xff0c;在VSCODE基本上都是这样的操作&#xff0c;不是…