进程的创建:fork()

news2025/2/28 15:30:34

引入

创建进程的方式我们已经学习了一个!在我们运行指令(或者运行我们自己写的可执行程序)的时候不就是创建了一个进程嘛?那个创建进程的方式称为指令级别的创建子进程!
那如果我们想要在代码中创建进程该怎么办呢?

fork()

fork 函数的使用,见见猪跑

这是一个系统调用函数,我们可以使用 man 指令来查看函数的说明文档!
在这里插入图片描述
在这里插入图片描述

介绍:这个函数可以为调用这个函数的进程创建一个进程,我们把这个新创建出来的进程叫做子进程,调用这个函数的进程称为父进程!
返回值:如果成功创建子进程,子进程的 PID 将被返回给父进程,0 将被返回给子进程;如果创建子进程失败,-1 将返回给父进程,错误码将被设置!

好的,我们不管这么多,先来用一用!

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>

int main()
{
    pid_t id = fork();
    if(id == 0) //子进程
    {
        while(1)
        {
            printf("我是子进程, pid: %d, ppid: %d\n", getpid(), getppid());
            sleep(1);
        }
    }
    else if(id > 0) //父进程
    {
        while(1)
        {
            printf("我是父进程, pid: %d\n", getpid());
            sleep(1);
        }
    }
    else // 子进程创建失败
    {
        perror("fork():");
    }

    return 0;
}

在上面的代码中,我们创建了一个子进程,让子进程循环打印自己的 pidppid,让父进程循环打印自己的 pid 我们来验证一下通过 fork 函数创建出来的进程到底是不是调用该函数进程的子进程。
在这里插入图片描述

我们看到子进程的 ppid 是 4176994,父进程的 pid 是4176994。说明我们的结论没有问题呢!

看到这里,你可能会有很多问题🤔~不着急我们一个一个来解决!

问题一:

为什么 fork 要给子进程返回 0,给父进程返回子进程的 pid

你想啊!一个进程只能调用一次 fork 函数嘛?显然不是的!我循环调用 fork 一百次,那么父进程应该如何区分这么多的子进程呢?那还不得靠返回值啦!
因此,fork 函数返回不同的值就是为了让父进程能够区分自己创建的子进程,从而让不同的执行流执行不同的代码!

问题二:

fork 函数究竟在干什么?干了什么?

我们在进程的概念部分知道了:进程 = PCB (进程控制块, Linux 环境下叫 task_struct) + 代码和数据。这也就意味着,task_struct 中必然维护着指针信息,能够通过 task_struct 找到进程的代码和数据!因为 linux 操作系统对进程的管理,本质上是对 task_struct 的管理。CPU 要执行进程的代码必须能通过 task_struct 找到进程的代码和数据!


fork 创建子进程的时候,操作系统首先为子进程创建 task_struct 结构体,并初始化结构体中的属性~但是,在初始化指向子进程代码和数据的指针的时候,应该怎么办呢?因为子进程并没有自己的代码和数据哇!那操作系统就说啦,子进程不是父进程创建的嘛,就让这个指针指向父进程的代码和数据吧!


于是,我们得出了一个重要的结论:fork 之后,父子进程的代码共享
在这里插入图片描述

父进程为什么要创建子进程,不就是想让子进程来帮忙的嘛!因此,为了让父子进程执行不同的代码,就需要通过 fork 不同的返回值来实现!

问题三:

一个变量怎么会有不同的内容?如何理解?

在任何操作系统中,进程在运行的时候具有独立性!
其实根据常识也能证明:你的电脑上同时运行着 QQ 和 微型这两个进程!突然 QQ 这个进程挂掉了!QQ 挂掉了会影响微信这个进程的运行嘛?显然是不会的!

在这里插入图片描述
在来看这张图,我们说 fork 之后,父子进程的代码和数据是共享的,我们又说进程之间是互相独立的!假设我们的子进程想要修改父进程中的数据怎么办呢?这种操作会被允许嘛?
我们先来写一个代码看看结论!

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>

int g_val = 100;

int main()
{
    pid_t id = fork();
    if(id == 0) //子进程
    {
        int cnt = 0;
        while(1)
        {
            printf("我是子进程, g_val = %d\n", g_val);
            cnt++;
            if(cnt == 3)
            {
                printf("change g_val\n");
                g_val = 200;
            }
            sleep(1);
        }
    }
    else if(id > 0) //父进程
    {
        while(1)
        {
            printf("我是父进程, g_val: %d\n", g_val);
            sleep(1);
        }
    }
    else // 子进程创建失败
    {
        perror("fork():");
    }

    return 0;
}

在上面的代码中我们定义了一个全局变量 g_val 在父子进程中每隔一秒打印 g_val 的值。在子进程中 3 秒之后将 g_val 修改了,我们观察父子进程打印 g_val 的结果有什么变化!
在这里插入图片描述
我们看到在子进程中,g_val 变成了 200,父进程中 g_val 还是 100。这是为什么呢?我们知道进程之间是具有独立性的!因为数据可能会被修改,这就注定了父子进程之间的数据是不能共享的
那怎么办呢?在创建子进程的时候将父进程的数据拷贝一份给子进程?这样做的确没有任何问题!但是如果子进程都不对父进程的数据做修改,这不就白白给子进程拷贝了一份数据嘛!造成内存负担


于是操作系统说:当子进程要修改父进程的数据时,我再给你子进程拷贝数据吧!这个行为被称为:父子进程数据层面的写时拷贝。当操作系统检测到子进程要修改父进程的数据时,会为子进程重新分配一块内存空间!


因为代码不可能被修改,父子进程代码共享并不影响进程之间的独立性!

问题四:

一个函数是如何做到返回两次的?怎么理解?

首先,fork 是一个函数,在这个函数中负责为调用他的进程创建子进程,这个函数体的实现一定包含但不限于以下操作:

  • 创建子进程的 task_struct
  • 填充 task_struct 的内容。
  • 父子进程指向相同的代码。
  • 修改子进程的状态。等等

fork 这个函数执行到 return 语句的时候,此时子进程一定已经被创建出来了!并且父子进程指向了相同的代码!而 return 本身也是代码哇!我们的代码:pid_t id = fork()return 的本质不就是在向 id 这个变量中写入吗 (return 返回时,先把返回值写到 cpu 中的寄存器中,最后再把寄存器中的值拷贝到你接收到的变量中!)?子进程此时要修改 id 中的内容,是不是就得发生写时拷贝!因此,同一个 id 变量会有两个不同的值。

问题五:

如果父子进程被创建好,谁先运行?

答案是:不清楚,谁先运行由调度器决定!

问题六:

同一个变量名存储不同的数据,如何做到?

这个问题仙子阿没打讲解,我们等到学习进程地址空间的时候再说吧!

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

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

相关文章

SSM框架(五):Maven进阶

文章目录 一、分模块开发1.1 分模块开发的意义1.2 步骤 二、依赖管理2.1 依赖传递2.2 可选依赖和排除依赖 三、继承与聚合3.1 聚合3.2 继承3.3 聚合和继承区别 四、属性4.1 pom文件的依赖使用属性4.2 资源文件使用属性 五、多环境开发六、跳过测试七、私服7.1 下载与使用7.2 私…

禁止谷歌浏览器自动更新

禁止谷歌浏览器自动更新 在使用Python包selenium的时候浏览器版版本发生变化后产生很多问题如&#xff1a; 1、直接版本不对应无法运行 2、版本不一致导致debug启动浏览器超级慢 这里是已谷歌浏览器为代表的。 禁止自动更新的方法如下&#xff1a; 1、WinR调出运行&#x…

一小时玩转【负载均衡】

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

SpringBoot 是如何启动一个内置的Tomcat

为什么说Spring Boot框架内置Tomcat 容器,Spring Boot框架又是怎么样去启动Tomcat的?我简单总结下学习过程。 一:简单了解SpringBoot的启动类 我们都知道Spring Boot框架的启动类上是需要使用 @SpringBootApplication 注解标注的, @SpringBootApplication 是一个复合注解…

Redis--10--Pipeline

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Pipeline举例比较普通模式与 PipeLine 模式小结&#xff1a; Pipeline 前面我们已经说过&#xff0c;Redis客户端执行一条命令分为如下4个部分:1&#xff09;发送命…

前端入门(五)Vue3组合式API特性

文章目录 Vue3简介创建Vue3工程使用vite创建vue-cli方式 常用 Composition API启动项 - setup()setup的执行时机与参数 响应式原理vue2中的响应式vue3中的响应式ref函数reactive函数reactive与ref对比 计算属性 - computed监视属性 - watchwatchEffect Vue3生命周期自定义hook函…

滑动平均窗口的定义,优点,缺点,以及目前的应用!!

文章目录 前言一、滑动平均窗口的优点二、滑动平均窗口的缺点三、滑动平均窗口的应用 前言 滑动平均窗口是一种数据处理方法&#xff0c;它以固定的窗口大小对数据进行移动&#xff0c;并在每个窗口内计算数据的平均值。这种方法主要用于平滑数据&#xff0c;减小数据波动的影…

基于SpringBoot的仓库管理系统设计与实现附带源码和论文

博主24h在线&#xff0c;想要源码文档部署视频直接私聊&#xff0c;全网最低价&#xff0c;9.9拿走&#xff01; 【关键词】仓库管理系统&#xff0c;jsp编程技术&#xff0c;mysql数据库&#xff0c;SSM&#xff0c;Springboot 目 录 摘 要 Abstract 第1章 绪论 1.1 课题…

阿里云服务器跨区域迁移(多数据盘)

方法一. 复制镜像&#xff0c;共享镜像&#xff08;只有系统盘没有数据盘的情况&#xff01;&#xff09; 正常阿里云同区域服务器迁移只需要选择共享镜像即可&#xff0c;但是由于新老服务器区域限制所以需要先复制到新服务器区域再进行共享 选择服务器实例先创建后复制 比如…

Linux 内核源码各版本下载

下载地址&#xff1a; kernel/git/stable/linux.git - Linux kernel stable treehttps://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/refs/ 1. Linux 内核的基本概念&#xff1a; 内核是什么&#xff1f; 内核是操作系统的核心部分&#xff0c;负责管理系统…

网络虚拟化场景下网络包的发送过程

网络虚拟化有和存储虚拟化类似的地方&#xff0c;例如&#xff0c;它们都是基于 virtio 的&#xff0c;因而在看网络虚拟化的过程中&#xff0c;会看到和存储虚拟化很像的数据结构和原理。但是&#xff0c;网络虚拟化也有自己的特殊性。例如&#xff0c;存储虚拟化是将宿主机上…

物流单管理系统软件物流单打印,物流单打印模板,佳易王物流快运单管理软件下载

物流单管理系统软件物流单打印&#xff0c;物流单打印模板&#xff0c;佳易王物流快运单管理软件下载 软件试用版下载或技术支持可以点击最下方官网卡片 上图&#xff1a;在物流开单时&#xff0c;可以先输入电话&#xff0c;如果之前存在该托运人信息&#xff0c;则可以一键…

hbase Master is initializing

问题如下&#xff1a; ERROR: org.apache.hadoop.hbase.PleaseHoldException: Master is initializing ERROR: org.apache.hadoop.hbase.PleaseHoldException: Master is initializingat org.apache.hadoop.hbase.master.HMaster.checkInitialized(HMaster.java:2452)at org.…

P1 什么是链表 C语言简单易懂

目录 前言 01 什么是链表 02 数组的特点 03 数组的缺点 3.1 删除数组其中一个元素 3.2 数组增加某个节点 04 链表 前言 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《 C 》✨✨✨ &#x1f525; 推荐专栏2: 《 Linux C应用编程&#xff08;概念…

Web自动化测试详解

做测试的同学们都了解&#xff0c;做Web自动化&#xff0c;我们主要用Selenium或者是QTP。 有的人可能就会说&#xff0c;我没这个Java基础&#xff0c;没有Selenium基础&#xff0c;能行吗&#xff1f;测试虽然属于计算机行业&#xff0c;但其实并不需要太深入的编程知识&…

神经网络 代价函数

神经网络 代价函数 首先引入一些便于稍后讨论的新标记方法&#xff1a; 假设神经网络的训练样本有 m m m个&#xff0c;每个包含一组输入 x x x和一组输出信号 y y y&#xff0c; L L L表示神经网络层数&#xff0c; S I S_I SI​表示每层的neuron个数( S l S_l Sl​表示输出…

论文编写软件latex安装教程

目录 1.下载安装包2.安装texlive 本人系统为windows&#xff0c;本教程基于windows系统&#xff0c;如果是其它系统请参考对应教程&#xff0c;注意选择对应系统的安装包&#xff01; 1.下载安装包 有三种集成环境安装包 texlive 是主流的环境&#xff0c;集成了较多的包&…

百度飞桨(张江)人工智能产业赋能中心入驻申请

中心如何赋能 百度飞桨&#xff08;张江&#xff09;人工智能产业赋能中心是浦东“大企业开放创新中心计划”首批企业代表&#xff0c;百度和张江集团将联合为入驻初创企业及生态合作伙伴提供以下服务&#xff1a; 降低AI使用门槛 通过“百度飞桨人工智能公共技术平台”&#x…

基于SpringBoot的企业客户管理系统的设计与实现

摘 要 本论文主要论述了如何使用JAVA语言开发一个企业客户管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述企业客户管理系统的当前背景以及系统开发的目…

二维粒子群算法航线规划

GitHub - gabrielegilardi/PathPlanning: Implementation of particle swarm optimization (PSO) for path planning when the environment is known.