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)
        {
            //child
            printf("我是子进程;pid:%d,  ppid:%d, id=%d  正在执行下载任务\n",getpid(),getppid(),id);
        sleep(1);
        }
    }
    else
    {
        while(1)
        {
            //parent
            printf("我是父进程;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)
        {
            //child
            printf("我是子进程;pid:%d,  ppid:%d, id=%d  正在执行下载任务\n",getpid(),getppid(),id);
        sleep(1);
        }
    }
    else
    {
        while(1)
        {
            //parent
            printf("我是父进程;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的地址绝不是物理地址

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python 基础】-- 在 mac OS 中安装 多个 python 版本

目录 1、需求 2、实现 2.1 安装 pyenv 2.2 安装 pyenv-virtualenv 2.3 配置环境变量 2.4 创建 python 3.9.9 的环境 2.5 激活环境&#xff0c;在当前项目目录中使用&#xff0c;即执行 python 1、需求 由于项目所依赖的 python 版本有多个&#xff0c;需要在不同的 pyth…

axios请求封装

http.js // untils / http.js //导入封装好的axios实例 import request from ./requestconst http {get(url, params) {const config {method: get,url: url}if (params) config.params paramsreturn request(config)},post(url, params) {const config {method: post,url…

深入浅出RPC:选取适合自己的RPC

文章目录 1、RPC概念&&背景1.1、RPC背景 1.2、RPC是什么&#xff0c;什么时候需要用到&#xff1f;2、进程间的通信 - IPC与RPC2.1、什么是IPC2.2、IPC与RPC联系 3、RPC的实现3.1、RPC实现的基本思路3.2、RPC实现的扩展方向 4、RPC的选择 1、RPC概念&&背景 1.…

Elasticsearch——快速入门

从零基础的内容开始介绍Elasticsearch&#xff0c;主要包含以下内容&#xff1a; Elasticsearch的定义、优点&#xff0c;以及典型的业务场景。Elasticsearch中重要的概念。Elasticsearch典型的接入方式。安装Elasticsearch。使用Kibana调试Elasticsearch。Elasticsearch节点的…

【LeetCode刷题笔记(8-3)】【Python】【接雨水】【双指针】【困难】

文章目录 引言接雨水题目描述提示 解决方案3&#xff1a;【双指针】结束语 接雨水 【LeetCode刷题笔记&#xff08;8-1&#xff09;】【Python】【接雨水】【动态规划】【困难】 【LeetCode刷题笔记&#xff08;8-2&#xff09;】【Python】【接雨水】【单调栈】【困难】 引言…

从如何使用到如何实现一个Promise

promise是什么&#xff1f;主要用来解决什么问题&#xff1f; Promise是异步编程的一种解决方案&#xff0c;比传统解决方案--回调函数和事件--更合理更强大。 Promise特点&#xff1a; &#xff08;1&#xff09;对象的状态不受外界影响。Promise对象代表一个异步操作&…

ModuleNotFoundError: No module named ‘openai.error‘

ModuleNotFoundError: No module named ‘openai.error’ result self.fn(*self.args, **self.kwargs) File “H:\chatGPTWeb\chatgpt-on-wechat\channel\chat_channel.py”, line 168, in _handle reply self._generate_reply(context) File “H:\chatGPTWeb\chatgpt-on-wec…

2023_Spark_实验二十九:Flume配置KafkaSink

实验目的&#xff1a;掌握Flume采集数据发送到Kafka的方法 实验方法&#xff1a;通过配置Flume的KafkaSink采集数据到Kafka中 实验步骤&#xff1a; 一、明确日志采集方式 一般Flume采集日志source有两种方式&#xff1a; 1.Exec类型的Source 可以将命令产生的输出作为源&…

性能加速包: SpringBoot 2.7JDK 17,你敢尝一尝吗 | 京东物流技术团队

前言 众所周知&#xff0c;SpringBoot3.0迎来了全面支持JDK17的局面&#xff0c;且最低支持版本就是JDK17&#xff0c;这就意味着&#xff0c;Spring社区将完全抛弃JDK8&#xff0c;全面转战JDK17。作为JAVA开源生态里的扛把子&#xff0c;Spring可以说是整个JAVA生态的风向标…

(8)Linux Makefile | 依赖关系,依赖方法

&#x1f4ad;前言&#xff1a; 本篇文章会着重讲解Linux中的自动化构建代码工具: make/makefile的介绍与使用。 在Linux下编译代码时,每次都会输入 gcc code.c -o code.exe在删除可执行程序时,每次都会输入 rm -rf code.exe这样非常的不方便,很麻烦,于是乎学习自动化构建代…

原来Python的协程有2种实现方式

什么是协程 在 Python 中&#xff0c;协程&#xff08;Coroutine&#xff09;是一种轻量级的并发编程方式&#xff0c;可以通过协作式多任务来实现高效的并发执行。协程是一种特殊的生成器函数&#xff0c;通过使用 yield 关键字来挂起函数的执行&#xff0c;并保存当前的执行…

《Effective C++》学习笔记 续

条款31&#xff1a;将文件间编译依存关系降至最低 请记住&#xff1a; 支持”编译依存性最小化“的一般构想是&#xff1a;相依于声明式&#xff0c;不要相依于定义式。基于此构想的两个手段是Handle class和Interface class程序库头文件应该以”完全且仅有声明式“的形式存在…

uniapp 用于开发H5项目展示饼图,使用ucharts 饼图示例

先下载ucharts H5示例源码&#xff1a; uCharts: 高性能跨平台图表库&#xff0c;支持H5、APP、小程序&#xff08;微信小程序、支付宝小程序、钉钉小程序、百度小程序、头条小程序、QQ小程序、快手小程序、360小程序&#xff09;、Vue、Taro等更多支持canvas的框架平台&#…

网络安全之Linux环境配置及Linux基础知识讲解<三>

目录 一.下载安装Vmware二.下载安装Kali三.Linux目录结构四.Linux文件属性五.文件目录管理六.vim编辑器 一.下载安装Vmware Vmware官网&#xff1a;https://www.vmware.com 二.下载安装Kali Kali包含数百种工具&#xff0c;可用于各种信息安全任务&#xff0c;例如渗透测试、…

(C++)将x减到0的最小操作数--滑动窗口

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://le…

微机总线地址物理内存地址虚拟内存地址简介

硬件地址的相关概念 Raspberry Pi 发布适用于 ARM 外设的 BCM2835 数据表 地址映射 总线地址 物理地址 虚拟地址 页表和内存管理单元MMU 《 Linux内核设计与实现&#xff08;第三版&#xff09;》 树莓派博通BCM2835芯片手册 硬件地址的相关概念 总线地址 32位的操作系统 &…

【赠书活动】OpenCV4工业缺陷检测的六种方法

文章目录 前言机器视觉缺陷检测工业上常见缺陷检测方法延伸阅读推荐语 赠书活动 前言 随着工业制造的发展&#xff0c;对产品质量的要求越来越高。工业缺陷检测是确保产品质量的重要环节&#xff0c;而计算机视觉技术的应用能够有效提升工业缺陷检测的效率和精度。 OpenCV是一…

【机器学习】卷积神经网络(CNN)的特征数计算

文章目录 基本步骤示例图解过程 基本步骤 在卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;计算最后的特征数通常涉及到以下步骤&#xff1a; 确定输入尺寸&#xff1a; 首先&#xff0c;你需要知道输入数据的尺寸。对于图像数据&#xff0c;这通常是 (batch_size, c…

1-完全理解以太坊智能合约

了解区块链 区块链技术的核心概念是分布式账本&#xff0c;它是许多参与者共享的特定类型的数据库。 这个特殊的数据库只是一个交易列表&#xff0c;记录着网络中发生的每笔交易。每个人都可以拥有自己的交易列表备份&#xff0c;再加上强有力的货币激励措施消除各方之间信任…

记录今日将C语言的Windows程序更改为python语言Windows程序,实现子窗口控制,类似微信程序框架最简单的原型

基本思路 为什么要选择python制作Windows应用程序&#xff0c;主要就是源代码直接展示&#xff0c;发现问题随时修改&#xff0c;同时可以不断增加新的功能方便。 由于C语言的Windows程序中结构类型在python中不能使用&#xff0c; 因此我们按照ctypes模块指导意见继承structu…