当前位置: 首页 > news >正文

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

💓博主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进程状态信息 🔍

相关文章:

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

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; 进程状态管理 1. 前言2. 查看…...

数字IC前端学习笔记:数字乘法器的优化设计(华莱士树乘法器)

相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 进位保留乘法器依旧保留着阵列的排列规则&#xff0c;只是进位是沿斜下角&#xff0c;如果能使用树形结构来规划这些进位保留加法器&#xff0c;就能获得更短的关键…...

CountDownLatch 批量更改使用,

代码 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.first.pet.platform.entity.PlatformAddress; import com.first.pet.platform.mapper.PlatformAddressMapper; …...

910数据结构(2019年真题)

算法设计题 问题1 有一种排序算法叫做计数排序。这种排序算法对一个待排序的表(采用顺序存储)进行排序,并将排序结果存放到另一个新的表中。必须注意的是,表中所有待排序的关键字互不相同,计数排序算法针对表中的每个元素,扫描待排序的表一趟,统计表中有多少个元素的关…...

推荐系统实践 笔记

诸神缄默不语-个人CSDN博文目录 这是我2020年写的笔记&#xff0c;我从印象笔记搬过来公开。 如果那年还在读本科的同学也许有印象&#xff0c;那年美赛出了道根据电商评论给商户提建议的题。其实这件事跟推荐系统关系不大&#xff0c;但我们当时病急乱投医&#xff0c;我打开…...

【JavaEE】JUC(Java.util.concurrent)常见类

文章目录 前言ReentrantLock原子类线程池信号量CountDownLatch相关面试题 前言 经过前面文章的学习我们大致了解了如何实现多线程编程和解决多线程编程中遇到的线程不安全问题&#xff0c;java.util.concurrent 是我们多线程编程的一个常用包&#xff0c;那么今天我将为大家分…...

清除浮动的方法

为什么需要清除浮动&#xff1f; 父级的盒子不能把height定死这样&#xff0c;浮动子类就没有了&#xff08;行内块元素的特点&#xff09;&#xff0c;父类高度为零。故引用清除浮动 1、父级没有高度 2、子盒子浮动了 3、影响下面的布局了&#xff0c;我们就应该清除浮动了…...

LangChain 摘要 和问答示例

在Azure上的OpenAI端点 注意 OpenAI key 可以用微软 用例【1. 嵌入 &#xff0c;2. 问答】 1. import os import openai from langchain.embeddings import OpenAIEmbeddings os.environ["OPENAI_API_KEY"] "****" # Azure 的密钥 os.environ["OP…...

(32)测距仪(声纳、激光雷达、深度摄影机)

文章目录 前言 32.1 单向测距仪 32.2 全向性近距离测距仪 32.3 基于视觉的传感器 前言 旋翼飞机/固定翼/无人车支持多种不同的测距仪&#xff0c;包括激光雷达&#xff08;使用激光或红外线光束进行距离测量&#xff09;、360 度激光雷达&#xff08;可探测多个方向的障碍…...

教你拥有一个自己的QQ机器人!0基础超详细保姆级教学!基于NoneBot2 Windows端搭建QQ机器人

0.序言 原文链接&#xff1a;教你本地化部署一个QQ机器人本教程主要面向Windows系统用户教程从0开始全程详细指导&#xff0c;0基础萌新请放心食用&#x1f355;如果你遇到了问题&#xff0c;请仔细检查是否哪一步有遗漏。如果你确定自己的操作没问题&#xff0c;可以到原文链…...

智能银行卡明细筛选与统计,轻松掌握账户总花销!

作为现代生活的重要组成部分&#xff0c;银行卡成为了我们日常消费和收入的主要途径。但是&#xff0c;当我们需要了解自己的银行卡账户的总花销时&#xff0c;繁琐的明细筛选和统计工作常常让人头疼。现在&#xff0c;让我们向您推荐一款智能银行卡明细筛选与统计工具&#xf…...

SRT服务器SLS

目前互联网上的视频直播有两种&#xff0c;一种是基于RTMP协议的直播&#xff0c;这种直播方式上行推流使用RTMP协议&#xff0c;下行播放使用RTMP&#xff0c;HTTPFLV或者HLS&#xff0c;直播延时一般大于3秒&#xff0c;广泛应用秀场、游戏、赛事和事件直播&#xff0c;满足了…...

Linux 安装 Android SDK

先安装jdk RUN apt-get install default-jdk 参考&#xff1a;http://t.zoukankan.com/braveym-p-6143356.html mkdir -p $HOME/install/android-sdk wget https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip unzip commandlinetools-linu…...

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件

本章要实现的整体效果如下&#xff1a; QEvent::MouseButtonPress ​ 鼠标按下时&#xff0c;触发该事件&#xff0c;它对应的子类是 QMouseEvent QEvent::MouseMove ​ 鼠标移动时&#xff0c;触发该事件&#xff0c;它对应的子类是 QMouseEvent QEvent::MouseButtonRel…...

vue3父子通信+ref,toRef,toRefs使用实例

ref是什么? 生成值类型的响应式数据可用于模板和reactive通过.value修改值可以获取DOM元素 <p ref”elemRef”>{{nameRef}} -- {{state.name}}</p> // 获取dom元素 onMounted(()>{ console.log(elemRef.value); }); toRef是什么? 针对一个响应式对象(rea…...

输入电压转化为电流性 5~20mA方案

输入电压转化为电流性 5~20mA方案 方案一方案二方案三 方案一 XTR111是一款精密的电压-电流转换器是最广泛应用之一。原因有二&#xff1a;一是线性度非常好、二是价格便宜。总结成一点&#xff0c;就是性价比高。 典型电路 最终电路 Z1二极管处输出电流表达式&#xff1a;…...

SpringBoot自带模板引擎Thymeleaf使用详解①

目录 前言 一、SpringBoot静态资源相关目录 二、变量输出 2.1 在templates目录下创建视图index.html 2.2 创建对应的Controller 2.3 在视图展示model中的值 三、操作字符串和时间 3.1 操作字符串 3.2 操作时间 前言 Thymeleaf是一款用于渲染XML/HTML5内容的模板引擎&am…...

推荐算法——Apriori算法原理

0、前言&#xff1a; 首先名字别读错&#xff1a;an pu ruo ao rui 【拼音发音】Apriori是一种推荐算法推荐系统&#xff1a;从海量数据中&#xff0c;帮助用户进行信息的过滤和选择。主要推荐方法有&#xff1a;基于内容的推荐、协同过滤推荐、基于关联规则的推荐、基于知识的…...

vue ant 隐藏 列

vue ant 隐藏 列 如果你使用的是Vue和Ant Design Vue组件库&#xff0c;你可以使用v-if指令来实现条件渲染来隐藏列。以下是一个示例代码&#xff1a; <template><a-table :columns"columns" :data-source"data"><template v-slot:custom…...

java基础之初始化顺序

初始化顺序 在类中变量定义的顺序决定了它们初始化的顺序。在创建任何java对象时&#xff0c;都是依次调用父类非静态初始化块、父类构造器执行初始化、本类的非静态初始化块、本类构造器执行初始化 public class House { // 构造器之前 Window w1 new Window(1); Ho…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

WEB3全栈开发——面试专业技能点P7前端与链上集成

一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染&#xff08;SSR&#xff09;与静态网站生成&#xff08;SSG&#xff09; 框架&#xff0c;由 Vercel 开发。它简化了构建生产级 React 应用的过程&#xff0c;并内置了很多特性&#xff1a; ✅ 文件系…...

Monorepo架构: Nx Cloud 扩展能力与缓存加速

借助 Nx Cloud 实现项目协同与加速构建 1 &#xff09; 缓存工作原理分析 在了解了本地缓存和远程缓存之后&#xff0c;我们来探究缓存是如何工作的。以计算文件的哈希串为例&#xff0c;若后续运行任务时文件哈希串未变&#xff0c;系统会直接使用对应的输出和制品文件。 2 …...

Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程

鸿蒙电脑版操作系统来了&#xff0c;很多小伙伴想体验鸿蒙电脑版操作系统&#xff0c;可惜&#xff0c;鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机&#xff0c;来体验大家心心念念的鸿蒙系统啦&#xff01;注意&#xff1a;虚拟…...

Redis上篇--知识点总结

Redis上篇–解析 本文大部分知识整理自网上&#xff0c;在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库&#xff0c;Redis 的键值对中的 key 就是字符串对象&#xff0c;而 val…...