【linux进程(二)】如何创建子进程?--fork函数深度剖析

news2024/12/24 20:19:58

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:Linux从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学更多操作系统知识
  🔝🔝


在这里插入图片描述

进程状态管理

  • 1. 前言
  • 2. 查看进程的第二种方式
  • 3. 如何创建一个子进程?
  • 4. fork函数详解(一)
  • 5. fork函数详解(二)
  • 6. fork函数详解(三)
  • 7. fork函数详解(四)
  • 8. fork函数详解(五)
  • 9. 总结以及拓展

1. 前言

我们已经会使用getpid/getppid
函数来查看pid和ppid了,本篇文章
会介绍第二种查看进程的方式

本章重点:

本篇文章着重介绍创建子进程
的函数:fork的概念以及返回值
本篇文章主要解决以下问题:

  • fork函数干了什么事?
  • 为什么fork有两个返回值?
  • 为啥fork的返回给父子进程的内容不同?
  • fork之后,父子进程谁先运行?
  • 如何理解同一个变量有不同的值?

这些问题的答案会在文章中给出


2. 查看进程的第二种方式

在Linux系统中,有一个动态文件proc
它里面存放着所有进程的信息,之所以
叫动态文件是因为它会随着进程的改变
而随时更新它的内容!

查看所有进程文件:

使用指令: ls /proc/

查看特点的进程文件:

使用指令: ls /proc/pid

在这里插入图片描述

比如我现在写一个死循环代码
然后通过此文件来查看我这个进程:

查看动态文件

可以发现,在自行创建的进程中
有很多我们看不懂的文件,这些文件
也不需要掌握,但是有两个文件需要
大家注意,一个是cwd一个是exe

在这里插入图片描述

exe指向可执行程序的位置
cwd代表默认的当前文件

我们经常听见一句话:在当前文件
创建一个文件,在当前文件怎么怎么样
这个当前文件就是cwd指向的文件
并且Linux外壳的bash中,pwd指令
其实就是从cwd中找到当前路径的!


3. 如何创建一个子进程?

众所周知啊,Linux系统是用C语言写的
所以Linux中创建一个进程实际上也要
调用C语言的函数,也就是用代码创建
进程,用户使用代码创建进程叫系统调用

使用函数: fork

使用man指令查看fork函数信息:

在这里插入图片描述

写一段代码创建子进程观察情况:

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

请看下面的图片观察情况:
在这里插入图片描述

接下来再打一个死循环观察情况:

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

请看以下图片观察情况:
在这里插入图片描述

它会循环打印pid和ppid,可以发现
蓝色框里面的ppid明显是命令行解释器
bash的pid,这个进程的pid是31063
创建的子进程的pid是31064,并且子进程
的ppid也就是父亲id是31063,这就已经
说明了一个情况:fork之后,已经创建了子进程
并且此进程的父进程是我们自己写的程序!


4. fork函数详解(一)

通过上面的代码和图文,可以发现
fork之前的代码只有父进程执行
然而fork之后的代码父子进程都要执行

fork函数不仅会帮我们创建子进程
它还有两个返回值,父进程会接受到
子进程的pid,子进程会接收到值:0
那么你可能有疑问?既然fork之后
父子进程会执行一样的代码,那么子进程
的意义是什么?其实fork是这样用的:

int forkid = fork();
if(forkod==0)
{
	执行子进程的专有代码
}
else
{
	执行父进程的专有代码
}

实际上我们创建子进程的意义就是
为了让子进程执行和父进程不一样
的代码,实现和父进程不一样的功能
比如我们可以一边下载软件一边播放
音乐,这两个过程就是不同的进程在执行!

改修代码后查看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)//子进程的代码片段    
    {    
        while(1)    
        {    
            printf("我是子进程: pid:%d ppid: %d ret:%d,我在进行下载任务\n",getpid(),getppid(),id);    
            sleep(1);    
        }    
    }    
    else if(id>0)//父进程的代码片段    
    {    
    
        while(1)    
        {    
            printf("我是父进程: pid:%d ppid: %d ret:%d,我在进行播放任务\n",getpid(),getppid(),id);    
        sleep(1);    
        }    
    } 
	return 0;
}

请看下图观察情况:

在这里插入图片描述


5. fork函数详解(二)

观察上面的情况,fork函数到底做了什么?
现在我来回答这个问题:

fork会创建子进程,系统中会多出
一个子进程,操作系统以父进程为
模板为子进程创建PCB,但是子进程
中是没有代码和数据的,当前状态
子进程和父进程共享代码和数据
所以fork之后,父子进程会执行一样的代码

父子进程的关系可以用下图来理解:

在这里插入图片描述

理解了这一点后,第三点也很好理解
首先,一个父进程可以创建很多个子
进程,然而一个子进程只对应一个父
进程,所以fork函数会返回子进程的id
给父进程,方便父进程管理它的子进程

现在就已经解决了开头的第1.3问题了


6. fork函数详解(三)

现在我想来解答第二个问题,众所周知啊
C/C++函数只能有一个返回值,然而这里
的fork函数既然也是C函数,为什么会有两个
返回值呢?请看以下的解释:

首先,fork之后,父子进程都会执行
代码的本质是它们都被内存调度了
而当一个函数执行到return时,它的
核心工作才算执行完成,于是我们可以
想象一下fork函数内部的一些代码信息:

在这里插入图片描述

可以发现,在fork函数return之前,
就已经创建了子进程,并且将子进程
放入调度队列中运行了,所以当子进程
在调度队列时,它和父进程就已经分流了
而不是真正在fork函数return之后才分流的

并且创建完子进程后代码是共享的
很明显return也是一句代码,所以父子进程
都会执行return语句,fork函数有两个返回值


7. fork函数详解(四)

现在,我来回答第四个问题
fork之后,父子进程谁先运行?

在讲解第二问时我们知道,创建完成
子进程后,这只是一个开始,系统的其他
进程,父进程,子进程接下来会被调度执行!

问题是先调度谁?先创建就先调度吗?

答案明显不是!在调度队列中,CPU
会选择一个进程去运行它,谁先被调度
谁就先运行!所以fork之后父子进程谁
先运行用户是不确定的,这是由各自
进程PCB中的调度信息决定的,比如
优先级,算法信息等等

下面有一篇拓展阅读,有兴趣可以看看:

fork函数拓展阅读


8. fork函数详解(五)

最后来回答第五个问题
前面一个函数有两个返回值你可能
能够理解,因为两个进程都被调度了
但是同一个变量怎么可能有两个不同
的值呢?变量id在父进程和子进程中值不同

首先我们要清楚一点:

干掉父进程不会影响子进程运行,反之也是

请看下面的视频验证:

kill父进程不会影响子进程

通过以上实验看出,进程是有独立性的
首先是表现在进程各自的PCB运行时
不会相互影响,很明显,代码本身只是可读
的,所以不会影响代码,但是对于数据来说
父子的数据是可能不同的(可能会被修改)

所以系统是怎样做到让数据在各个
进程都自己私有一份的?答案是写时拷贝
类似于学习类和对象时的深浅拷贝
数据会在需要使用时被写时拷贝到PCB
然而fork返回值赋值给变量时,本质也是
写入,返回时也会发生写时拷贝,所以不同
的进程执行的代码中的变量id获取的值不同!


9. 总结以及拓展

fork函数的细节还有很多,但是以目前
的学习进度来说,要完全理解它很困难
所以文章采用了较为简单的方式帮助理解
只要理解了fork函数的五个问题的答案
那么在目前阶段就已经干的很好了!

拓展阅读:

Linux下的PCB源码解析


🔎 下期预告:Linux进程状态信息 🔍

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

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

相关文章

Nacos单机配置集群配置

大家好我是苏麟今天带来Nacos单机配置和集群配置 Nacos报错 理论上启动nacos&#xff0c;只需要双击startup.cmd文件&#xff08;win下&#xff09;&#xff0c;就可以直接启动&#xff0c;但是从GitHub上下载当前最新版Nocas(下载地址)&#xff0c;下载后进入bin目录双击sta…

生成Release版本的.pdb文件

软件分为Debug版本、Release版本这2种版本&#xff0c;其中Debug版本是带有.pdb调试信息文件&#xff0c;而Release版本不带.pdb调试信息文件。软件发布时&#xff0c;一般采用Release版本&#xff0c;若因内存泄漏、数组访问越界、除零错误、磁盘读写错误等异常&#xff0c;造…

PLL锁相环倍频原理

晶振8MHz&#xff0c;但是处理器输入可以达到72MHz&#xff0c;是因为PLL锁相环提供了72MHz。 锁相环由PD&#xff08;鉴相器&#xff09;、LP&#xff08;滤波器&#xff09;、VCO&#xff08;压控振荡器&#xff09;组成。 处理器获得的72MHz并非晶振提供&#xff0c;而是锁…

中国1km分辨率月最低温和最高温度数据集(1901-2020)

简介&#xff1a; 中国1km分辨率月最低温度数据集&#xff08;1901-2020&#xff09;是根据CRU发布的全球0.5气候数据集以及WorldClim发布的全球高分辨率气候数据集&#xff0c;通过Delta空间降尺度方案在中国地区降尺度生成的。使用了496个独立气象观测点数据进行验证&#x…

深度学习基础 2D卷积(1)

什么是2D卷积 2D参数量怎么计算 以pytorch为例子&#xff0c;2D卷积在设置的时候具有以下参数&#xff0c;具有输入通道的多少&#xff08;这个决定了卷积核的通道数量&#xff09;&#xff0c;滤波器数量&#xff0c;这个是有多少个滤波器&#xff0c;越多提取的特征就越有用…

如何使用 LeiaPix 让照片动起来

在过去&#xff0c;想要让照片动起来&#xff0c;需要使用专业的软件和技巧。但是&#xff0c;随着科技的发展&#xff0c;现在只需使用一个简单的工具&#xff0c;就可以轻松地让照片动起来。 LeiaPix 是一个免费的在线工具&#xff0c;可以将静态照片转换为动画。该工具使用…

六、【常用工具组】

文章目录 移动工具组移动工具画板工具&#xff1a; 路径选择工具组抓手工具组 移动工具组 移动工具 在不同图层中选择时&#xff0c;先点击对应图层&#xff0c;然后按住control键&#xff0c;再使用鼠标拖拽即可移动图层 按CtrlT选中图层&#xff0c;然后右键即可进行置入、…

好工具分享:阿里云价格计算器_一键计算精准报价

阿里云服务器价格计算器&#xff0c;鼠标选择云服务器ECS实例规格、地域、系统盘、带宽及购买时长即可一键计算出精准报价&#xff0c;阿里云服务器网分享阿里云服务器价格计算器链接地址&#xff1a; 阿里云服务器价格计算器 先打开阿里云服务器ECS页面 aliyunfuwuqi.com/go…

阿里云服务器IP地址查询方法(公网IP和私网IP)

阿里云服务器IP地址在哪查看&#xff1f;在云服务器ECS管理控制台即可查看&#xff0c;阿里云服务器IP地址包括公网IP和私有IP地址&#xff0c;阿里云百科分享阿里云服务器IP地址查询方法&#xff1a; 目录 阿里云服务器IP地址查询 阿里云服务器IP地址查询 1、登录到阿里云服…

常见的软件脱壳思路

单步跟踪法 1.本方法采用OD载入。 2.跟踪F8&#xff0c;实现向下的跳。 3.遇到程序回跳按F4。 4.绿色线条表示跳转没实现&#xff0c;不用理会&#xff0c;红色线条表示跳转已经实现&#xff01; 5.刚载入程序有一个CALL的&#xff0c;我们就F7跟进去&#xff0c;不然程序很容…

【C++】一文带你走入vector

文章目录 一、vector的介绍二、vector的常用接口说明2.1 vector的使用2.2 vector iterator的使用2.3 vector空间增长问题2.4 vector 增删查改 三、总结 ヾ(๑╹◡╹)&#xff89;" 人总要为过去的懒惰而付出代价ヾ(๑╹◡╹)&#xff89;" 一、vector的介绍 vector…

selenium自动化测试环境安装教程

0X00前言&#xff1a; Selenium是一个广泛应用于Web应用程序测试的工具。它提供了一组功能强大的API&#xff0c;用于模拟用户与Web浏览器的交互。以下是对Selenium的简要介绍&#xff1a; 功能&#xff1a;Selenium能够自动化执行各种Web浏览器上的操作&#xff0c;如点击、输…

qml保姆级教程五:视图组件

&#x1f482; 个人主页:pp不会算法v &#x1f91f; 版权: 本文由【pp不会算法v】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 QML系列教程 QML教程一&#xff1a;布局组件 文章目录 列表视图ListVi…

API基础————包

什么是包&#xff0c;package实际上就是一个文件夹&#xff0c;便于程序员更好的管理维护自己的代码。它可以使得一个项目结构更加清晰明了。 Java也有20年历史了&#xff0c;这么多年有这么多程序员写了无数行代码&#xff0c;其中有大量重复的&#xff0c;为了更加便捷省时地…

十天学完基础数据结构-第九天(堆(Heap))

堆的基本概念 堆是一种特殊的树形数据结构&#xff0c;通常用于实现优先级队列。堆具有以下两个主要特点&#xff1a; 父节点的值始终大于或等于其子节点的值&#xff08;最大堆&#xff09;&#xff0c;或者父节点的值始终小于或等于其子节点的值&#xff08;最小堆&#xff…

【2023年11月第四版教材】第18章《项目绩效域》(合集篇)

第18章《项目绩效域》&#xff08;合集篇&#xff09; 1 章节内容2 干系人绩效域2.1 绩效要点2.2 执行效果检查2.3 与其他绩效域的相互作用 3 团队绩效域3.1 绩效要点3.2 与其他绩效域的相互作用3.3 执行效果检查3.4 开发方法和生命周期绩效域 4 绩效要点4.1 与其他绩效域的相互…

深入了解 PostgreSQL:功能、特性和部署

PostgreSQL&#xff0c;通常简称为Postgres&#xff0c;是一款强大且开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它在数据存储和处理方面提供了广泛的功能和灵活性。本文将详细介绍 PostgreSQL 的功能、特性以及如何部署和使用它。 什么是 PostgreSQ…

C#,数值计算——完全VEGAS编码的蒙特·卡洛计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// Complete VEGAS Code /// adaptive/recursive Monte Carlo /// </summary> public abstract class VEGAS { const int NDMX 50; const int …

【HCIE】跨域MPLS-VPN Option C 方式一

实验目的&#xff1a;R5与R7私网互通&#xff1b;R6与R8私网互通 说明&#xff1a;R1PE1&#xff1b;R2ASBR1&#xff1b;R3-ASBR2&#xff1b;R4PE2&#xff1b;R5/R6/R7/R8CE 方式一图谱 步骤1&#xff1a;给R1 R9 R2 R3 R4 配置接口IP与环回IP &#xff08;略&#xff09; …