Linux--fork创建子进程详解

news2024/11/13 8:02:45

目录

一.初识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的地址绝不是物理地址

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

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

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

相关文章

SpringBoot2初始入门

适配器 任务调度 父项目、webstarter、bulid 版本管理 自动配置 Spring、SpringMVC 扫描包&#xff1a;主程序及其子包 底层注解&#xff1a; Configuration、Bean 单实例 proxyBeanMethodstrue&#xff0c;组件依赖 Import Conditioal&#xff08;name"")条件…

访问修饰符

1.java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围)。 1.公开级别:用public修饰,对外公开 2.受保护级别:用protected修饰,对子类和同一个包中的类公开 3.默认级别:没用修饰符,向同一个包的类公开 4.私有级别:用private修饰,只有本类可以访问,不…

智能优化算法应用:基于模拟退火算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于模拟退火算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于模拟退火算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.模拟退火算法4.实验参数设定5.算法结果6.…

如何处理好面试中的“压力测试”?

作为一名求职者&#xff0c;在面试时有时遇到的是压力测试&#xff0c;有时则遇到的是一些无良企业单位&#xff0c;究竟如何把握忍耐的限度&#xff0c;才合格当一个能经受压力的员工&#xff0c;才能避免对无良单位的一味隐忍! 压力面试是指有意制造紧张&#xff0c;以了解求…

VR播控系统深耕VR教学领域,助力开启未来新课堂

作为提升教育质量的技术之一&#xff0c;VR技术已经逐渐成为培养新一代人才、提升教学质量的重要方式&#xff0c;相比于传统教育&#xff0c;VR技术在教学方面的应用&#xff0c;所带来的变化和效果提升都是非常明显的&#xff0c;尤其是VR播控系统的上线&#xff0c;作为VR教…

MATLAB图解傅里叶变换(初学者也可以理解)

1、概述 相信很多人对于傅里叶变换可能觉得比较复杂和有点难懂&#xff0c;其实不难&#xff0c;它只是一种积分变换。 傅里叶变换&#xff0c;表示能将满足一定条件的某个函数表示成三角函数&#xff08;正弦和/或余弦函数&#xff09;或者它们的积分的线性组合。也就是说&qu…

gRPC框架

1、gRPC 与 Protobuf 介绍 微服务架构中&#xff0c;由于每个服务对应的代码库是独立运行的&#xff0c;无法直接调用&#xff0c;彼此间 的通信就是个大问题gRPC 可以实现微服务&#xff0c; 将大的项目拆分为多个小且独立的业务模块&#xff0c; 也就是服务&#xff0c; 各服…

DSP280049C初学(4)-FLASH烧录以及部分程序转移至RAM运行

DSP280049C初学&#xff08;4&#xff09;-FLASH烧录以及部分程序转移至RAM运行 实现目的&#xff1a;代码在RAM中调试完成后&#xff0c;就需要将其固化下载到FLASH中&#xff0c;但是FLASH中运行所有程序的话会存在计算或程序运行时间过长的问题&#xff0c;故还需要将部分代…

【C语言初阶】什么操作符你还没搞懂?试试这篇文章让你彻底理解各种操作符!

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 一、 算术操作符&#xff1a;1.1 加减乘除 二、 移位操作符&#xff1a;2.1 计算机中整数的存储2.2 >> 右…

arp欺骗原理以及实现方式

我们知道了arp的作用&#xff0c;那么此时我们怎么可以用他来进行攻击呢&#xff1f;在一个局域网中&#xff0c;我们怎么实现呢&#xff1f; 原理&#xff1a; 这样B就可以做到中间人了&#xff0c;可以接受到两个主机的数据了。换句话来说&#xff0c;在同一个局域网内&…

JVM 详解(JVM组成部分、双亲委派机制、垃圾回收算法、回收器、回收类型、了解调优思路)

目录 JVM 详解&#xff08;JVM组成部分、双亲委派机制、垃圾回收算法、回收器、回收类型、了解调优思路&#xff09;1、概念&#xff1a;什么是 JVM ?JVM 的作用&#xff1f; 2、JVM 的主要组成部分&#xff1f;类加载器&#xff08;Class Loader&#xff09;&#xff1a;简单…

坚鹏:美国智库认为中国在70%战略产业里领先,美国正迅速衰落

【重榜】2023年12月13日&#xff0c;美国智库信息技术与创新基金会&#xff08;ITIF&#xff09;发布重榜报告&#xff0c;认为中国在70%战略产业里领先&#xff0c;美国正迅速衰落。美国智库ITIF认为在计算机和电子产品、化工品、机器设备、机动车、基本金属、金属制品、电气设…

linux 网络子系统 摘要

当你输入一个网址并按下回车键的时候&#xff0c;首先&#xff0c;应用层协议对该请求包做了格式定义;紧接着传输层协议加上了双方的端口号&#xff0c;确认了双方通信的应用程序;然后网络协议加上了双方的IP地址&#xff0c;确认了双方的网络位置;最后链路层协议加上了双方的M…

精细化工ERP系统是什么?精细化工ERP软件包含哪些模块

化工是比较特殊的行业&#xff0c;日常的生产经营活动比较繁杂&#xff0c;传统的手工管理模式逐渐不能满足现代化工管理需求。进入信息化时代&#xff0c;如何顺应现代化工市场发展&#xff0c;是摆在很多化工企业面前的难题。 随着行业竞争愈加激烈&#xff0c;各种成本的上…

Word写大论文常见问题(持续更新)

脚注横线未定格 解决方案&#xff1a;“视图”-“草图”&#xff0c;“引用”-“显示备注”-选择“脚注分隔符”&#xff0c;把横线前的空格删掉。

基于Java SSM框架实现抗疫医疗用品销售系统项目【项目源码+论文说明】

基于java的SSM框架实现抗疫医疗用品销售系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;抗疫医疗用品销售平台当然也不能排除在外。抗疫医疗用品销售平台是以实…

亿欧网首届“元创·灵镜”科技艺术节精彩纷呈,实在智能AI Agent智能体展现硬核科技图景

12月4日-10日&#xff0c;持续一周的首届“元创灵镜”科技艺术节在海南陵水香水湾拉开帷幕&#xff0c;虚实交互创造出的“海岛之镜”开幕式呈现出既真实又虚幻的未来感&#xff0c;融入前沿科技元素的艺术装置作品在“虚实之镜&自然生长”科技艺术展诠释着浪漫想象&#x…

爬虫chrome浏览器抓包说明

chrome浏览器抓包说明 目标&#xff1a;掌握chrome在爬虫中的使用 1. 新建隐身窗口&#xff08;无痕窗口&#xff09; 作用&#xff1a;在打开无痕窗口的时候&#xff0c;第一次请求某个网站是没有携带cookie的&#xff0c;和代码请求一个网站一样&#xff0c;这样就能够尽可…

jpa 修改信息拦截

实现目标springbootJPA 哪个人&#xff0c;修改了哪个表的哪个字段&#xff0c;从什么值修改成什么值 Component // 必须加 Slf4j Configurable(autowire Autowire.BY_TYPE) public class AuditingEntityListener {// 线程变量&#xff0c;保存修改前的 objectprivate Thre…