Linux--fork创建子进程详解

目录

一.初识fork函数

二.fork的返回值

三.fork原理

1.fork是如何创建子进程的?

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

3.为什么父进程的返回值是子进程的pid,子进程返回值是0?

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

5.如何理解同一个变量,会有不同的值?


一.初识fork函数

创建子进程的方式:

1.在命令行上创建

2.在代码中使用fork创建

今天讲述的是fork创造子进程。

使用man手册查看fork

验证:fork()创建了子进程

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

观察图片,注意pid与ppid

在写一个循环情况观察:

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

从图中我们可以看出fork()之后的代码执行行了两次,根据pid与ppid,我们可以看出是两个不同进程执行的且可以分出那个是子进程,那个是父进程

结论:只有父进程执行了fork之前的代码,fork之后,父子进程都要执行后续代码

二.fork的返回值

fork函数的返回值:

当创建子进程成功时:

有两个返回值,子进程的返回值是0,父进程的返回值是子进程的pid

当创建子进程失败:

有一个返回值,小于0.

代码验证:

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

为什么fork函数的返回值要有两个呢?

这里我们就不得不提为什么创建子进程。

原因:我们想要子进程协助父进程完成一些单进程解决不了的工作。如:对于想要电影边下载,边播放(可能不恰当,但有助于理解)。

因此,我们想要子进程与父进程执行不一样的代码。为了实现这个这个目的,我们让fork的返回值有两个,然后通过判断fork的返回值,判断谁是父进程,谁是子进程,然后让他们执行不同的代码

代码实例:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>int main()
{printf("我是一个父进程,pid是:%d\n",getpid());pid_t id=fork();if(id<0)return 1;else if(id==0){while(1){//childprintf("我是子进程;pid:%d,  ppid:%d, id=%d  正在执行下载任务\n",getpid(),getppid(),id);sleep(1);}}else{while(1){//parentprintf("我是父进程;pid:%d,  ppid:%d, id=%d  正在执行播放任务\n",getpid(),getppid(),id);sleep(1);}}return 0;
}

三.fork原理

1.fork是如何创建子进程的?

我们知道

进程=可执行文件+内核数据结构(PCB)

在fork创建子进程时,系统会多一个子进程。

1.在内存中为子进程malloc申请空间并以父进程为模板,为子进程创建PCB并初始化(对父进程的PCB进行局部拷贝)

2.与父进程共享代码和数据.(这也是父子进程执行同样代码的原因)

这里我们知道了父子进程共享同一份代码与数据,那么为什么子进程不会执行fork之前的代码呢?

我们要知道程序之所以能够从上往下的顺序执行,依靠的是CPU中存在的pc/eip(程序计数器),当执行进程时,CPU会读取该进程的PCB里的pc/eip的值,然后从对应的代码开始执行。而fork创建子进程后,父进程的pc/eip指向了fork之后的代码,而进程的PCB在创建的时候刚好拷贝了父进程的pc/eip,因此子进程不会执行fork之前的代码。

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

这里问大家一个问题:

如果一个函数已经执行到return了,它的核心工作做完了吗?

这里我们可以以swap(int* x,int* y)函数为例,我们可以通过调试发现,在函数执行到return的时候,两个数的数值早已交换。

因此可以认为函数在执行到return时,它的工作就已经做完了

而fork就是一个系统调用函数,fork的工作如下:

因此,在fork执行到return的时候,进已经完成了子进程的创建了。我们知道父子进程共享代码,而return时代码,因此return也被父子进程共享执行。

所以

父进程被调度,就要执行return

子进程被调度,也要执行return

3.为什么父进程的返回值是子进程的pid,子进程返回值是0?

这里我们举现实生活的例子:

在现实生活中,一个父亲可以有多个子女,但是子女只有一个父亲。

父:子=1:n,所以我们有一个天然的需求,父进程要管理子进程,需要标识子进程的唯一性,而子进程访问父进程时不需要的。而又由于子进程的pid具有唯一性,因此采用了该方法

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

在创建完子进程之后,系统的其他进程,父进程和子进程,接下来是要被调度的

这里我们知道,操作系统是依靠双链表来管理进程的PCB的。

当父子进程的PCB都被创建并在运行队列中排队时,哪一个进程的PCB先被调度,那个进程就先运行。

而那个进程先被调度在用户层面上是不清楚的,因此我们并不知道父子进程谁先运行

因为它是由各自PCB中的调度信息(时间片,优先级)+调度器算法共同决定,完完全全又操作系统自主实现 

5.如何理解同一个变量,会有不同的值?

这里由于牵扯到地址空间,会在地址空间详细解释,这里只是一个开头!

在前面我们知道fork在成功创建子进程时,两个返回值。但是这里我们使用一个变量接受的,为什么一个变量,会有不同的值 ?!!!

这里我们先说一个现象。

如果启动一个QQ,微信,浏览器。这些都是进程,如果杀掉微信进程,QQ进程,浏览器进程还在吗?当然还在。

如果父子进程中,父进程被杀掉,子进程还在吗?或者反过来。

实例:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>int main()
{printf("我是一个父进程,pid是:%d\n",getpid());pid_t id=fork();if(id<0)return 1;else if(id==0){while(1){//childprintf("我是子进程;pid:%d,  ppid:%d, id=%d  正在执行下载任务\n",getpid(),getppid(),id);sleep(1);}}else{while(1){//parentprintf("我是父进程;pid:%d,  ppid:%d, id=%d  正在执行播放任务\n",getpid(),getppid(),id);sleep(1);}}return 0;
}

父进程被杀掉,子进程不受影响,仍可以正常运行

原因:进程进程之间运行的时候,是具有独立性的,无论是什么关系!

父子进程如何做到独立性,互不影响?

进程=可执行文件+内核数据结构(PCB)

1.父子进程有各自的PCB

2.由于代码是只读的,不会影响,数据数据父子是可以修改的。所以代码共享,数据各个进程要想办法私有一份。

而数据又是如何让私有的呢?

这里操作系统为了效率,采用了写时拷贝。

写时拷贝:在不修改数据时,父子进程共享数据,一旦修改数据,就会为修改数据的进程,重新开辟空间存储数据。

因此根据上述现象总结:

在fork函数的return处,子进程就创建完毕,开始共享代码与数据,而对于

id=fork();

这段代码,本质就是修改数据,因此发生写时拷贝,所以同一个变量会有不同的值!

那么具体是如何让一个变量的可以有两个值呢?

可能会有人觉得,只是变量名相同,地址不同。

这里我们可以用上面的代码将&id打印出来验证。

这里我们可以看出并不是的。

但是我们学过C语言,我们可以明确知道同一块地址空间,是不可能有不同内容的。

因此,我们可以确定:id的地址绝不是物理地址

接下来的内容由于牵扯到地址空间的内容,今天就讲到这里,还会在地址空间处详细讲解。

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

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

相关文章

【深度学习】强化学习(四)强化学习的值函数

文章目录 一、强化学习问题1、交互的对象2、强化学习的基本要素3、策略&#xff08;Policy&#xff09;4、马尔可夫决策过程5、强化学习的目标函数6、值函数1. 状态值函数&#xff08;State Value Function&#xff09;a. 状态值函数的定义b. 贝尔曼方程&#xff08;Bellman Eq…

亚马逊鲲鹏系统可快速创建大量的买家账户

在数字时代的浪潮中&#xff0c;人们总是在寻找更便捷、高效的方式来完成各种任务&#xff0c;而亚马逊鲲鹏系统的出现&#xff0c;无疑为那些渴望拥有大批量买家号的人提供了一个全新的可能性。在这个系统中&#xff0c;注册买家号变得轻而易举&#xff0c;只需准备好一些必要…

WebRTC AEC回声消除算法拆解

WebRTC AEC算法流程分析——时延估计&#xff08;一&#xff09; 其实&#xff0c;网上有很多类似资料&#xff0c;各个大厂研发不同应用场景设备的音频工程师基本都对其进行了拆解&#xff0c;有些闪烁其词&#xff0c;有些却很深奥&#xff0c;笔者随着对WebRTC了解的深入&a…

韵达快递查询入口,将途经指定城市的单号筛选出来

批量查询韵达快递单号的物流信息&#xff0c;并将途经指定城市的单号筛选出来。 所需工具&#xff1a; 一个【快递批量查询高手】软件 韵达快递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;第一次使用的伙伴记得先注册&#x…

生物分子相互作用的奥秘与挑战:探索未来药物设计的新方向

在生命科学领域&#xff0c;生物分子相互作用的研究日益受到关注。这种相互作用涉及蛋白质、核酸、脂质和糖类等生物分子之间的相互识别、结合和调控。理解这些相互作用对于揭示生命现象、疾病机制和治疗策略具有重要意义。 然而&#xff0c;生物分子相互作用的奥秘也带来了诸…

探索“超级服务器” TON:SDK 应用与开发入门

TON 是一个由多个组件构成的去中心化和开放的互联网平台&#xff0c;聚焦于实现广泛的跨链互操作性&#xff0c;同时在高可扩展性的安全框架中运作。TON 区块链被设计为分布式超级计算机或“超级服务器&#xff08;superserver&#xff09;”&#xff0c;旨在提供各种产品和服务…

在Node.js中MongoDB更新数据的方法

本文主要介绍在Node.js中MongoDB更新数据的方法。 目录 Node.js中MongoDB更新数据使用原生 MongoDB 驱动程序更新数据使用 Mongoose 更新数据 Node.js中MongoDB更新数据 在Node.js中&#xff0c;可以使用原生的 MongoDB 驱动程序或者使用 Mongoose 来更新 MongoDB 数据。 下面…

【腾讯云 HAI 域探秘】释放生产力:基于 HAI 打造团队专属的 AI 编程助手

文章目录 前言一、HAI 产品介绍二、HAI 应用场景介绍三、HAI 生产力场景探索&#xff1a;基于 HAI 打造团队专属的 AI 编程助手3.1 申请 HAI 内测资格3.2 购买 HAI 实例3.3 下载 CodeShell-7B-Chat 模型3.4 部署 text-generation-inference(TGI)推理服务3.4.1 下载 text-genera…

Linux 使用 Anaconda+Uwsgi 部署 Django项目和前端项目

一、安装Anaconda 使用Anaconda创建python环境的优点&#xff1a; virtualenv只能创建系统原有的python版本&#xff0c;而不能创建创建任意版本的环境 而Anaconda的虚拟环境中&#xff0c;你可以指定任意现存可使用的python环境&#xff08;包括比原环境版本高的python版本&a…

代码签名证书:数字安全世界的守门员

在这个信息化的时代&#xff0c;如果说互联网是高速流动的信息海洋&#xff0c;那么软件便是承载这些信息的庞大船队。而让人倍感安心地乘坐这些船的&#xff0c;正是被称为代码签名证书的重要安全措施。 你可以把代码签名证书想象成是软件世界的一位神秘守门员。它存在的目的&…

使用Python爬取公众号的合集

文章目录 前言讲解爬取思路开爬爬取文章url文章爬取结果爬取图片图片爬取结果优化下载图片代码 声明结尾关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游…

激活Windows过程及报错解决: 0x803f7001 在运行Microsoft Windows 非核心版本的计算机上, 运行“ slui.exe 0x2a 0x803f7001 “以显示错误文本

激活Windows过程及报错问题解决: 0x803f7001 在运行Microsoft Windows 非核心版本的计算机上&#xff0c;运行“ slui.exe 0x2a 0x803f7001 “以显示错误文本。 前言 最近在激活Windows过程中&#xff0c;遇到了报错: 0x803f7001 在运行Microsoft Windows 非核心版本的计算机上…

【数据分析与可视化】利用Python对学生成绩进行可视化分析实战(附源码)

需要源码和数据集请点赞关注收藏后评论区留言私信~~~ 下面对学生成句和表现等数据可视化分析 1&#xff1a;导入模块 import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt plt.rcParams[font.sans-serif][simhei] plt.rcParams[f…

【Android】使用 Glide 给 ImageView 加载图像的简单案例

前言 Android Glide是一个用于在Android应用中加载和显示图片的流行开源库。它提供了简单易用的API&#xff0c;可以帮助开发者高效地加载远程图片、本地图片以及GIF动画&#xff0c;并提供了缓存、内存管理等功能&#xff0c;使得图片加载在移动应用中更加流畅和高效。Glide还…

小白菜QQ云端机器人源码-去除解密授权

小白菜QQ云端机器人源码分享&#xff1a;解密授权学习版已去除 这款源码是专为群机器人爱好者设计的&#xff0c;它基于挂机宝机器人框架构建的网页站点。 用户可以通过网页登录QQ账号至挂机宝框架中&#xff0c;无需通过机器人实现登录。 而且&#xff0c;该源码解决了一个…

运筹学经典问题(三):最大流问题

问题描述 给定一个图网络 G ( V , E ) G(V, E) G(V,E)&#xff0c;网络中连边的权重代表最大容量&#xff0c;在这个图中找出从起点到终点流量最大的路径。 数学建模 集合&#xff1a; I I I&#xff1a;点的集合&#xff1b; E E E&#xff1a;边的集合。 常量&#x…

使用代理IP时的并发请求是什么意思?

很多做过数据采集的技术们应该都有所了解&#xff0c;在选择代理IP时会有一个并发请求的参数&#xff0c;这个参数是什么意思呢&#xff1f;可能有很多新手不是很了解&#xff0c;其实代理IP的并发请求就是指同时发送多个请求到目标服务器&#xff0c;以提高请求的效率和速度。…

docker-consul(容器的自动发现与注册)

1、微服务&#xff08;容器&#xff09;、容器的注册和发现&#xff1a;是一种分布式管理系统&#xff0c;用于定位服务的方法 &#xff08;1&#xff09;在传统的架构中&#xff0c;应用程序之间直连到已知的服务&#xff0c;设备提供的网络&#xff08;ip地址、基于tcp/ip的…

android 13.0 去掉recovery模式UI操作页面的菜单选项

1.概述 在13.0进行系统rom定制化开发中,在进行一些定制化开发中,会根据需要在进入recovery模式的时候,去掉recovery模式的一些菜单选项, Reboot to bootloader,Enter rescue等菜单项,经过分析得知, 就是在device.cpp去掉一些菜单选项就可以了,接下来就来分析实现相关功…

《PySpark大数据分析实战》-04.了解Spark

&#x1f4cb; 博主简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是wux_labs。&#x1f61c; 热衷于各种主流技术&#xff0c;热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员&#xff08;PCTA&#xff09;、TiDB数据库专家&#xff08;PCTP…