【Linux系统编程】五、进程创建 -- fork()
文章目录
- 前言
- Ⅰ. 重温fork函数
- 一、fork()的概念
- 二、如何理解fork()有两个返回值
- Ⅱ.fork的常规用法
- Ⅲ. fork调用失败的原因
- Ⅳ. 写时拷贝
- 为什么存在写时拷贝❓❓❓
前言
现阶段我们知道进程创建有如下两种方式,其实包括在以后的学习中这两种方式也是最常见的:
- 命令行启动命令 (程序、指令等)
- 通过程序自身
fork()后产生的子进程
Ⅰ. 重温fork函数
一、fork()的概念
在 linux 中 fork函数 是非常重要的 系统函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。之前我们通过查 man 手册,知道了运用 fork() 函数,并且调用它需要包含 头文件 <unistd.h>。
如下是函数的声明和返回值:
#include <unistd.h>
pid_t fork(void); // 返回值:子进程中返回0,父进程返回子进程id,出错返回-1
但是之前我们一直有个问题没有解决,那么就是**为什么一个函数一个返回两个值?**有了之前的进程概念的知识,这里我们就可以来解释一下这个现象了!
二、如何理解fork()有两个返回值
父进程 fork 时,子进程是以父进程为模板,简单地说就是子进程的大部分属性和属性值是拷贝父进程的,而小部分是指子进程的调度时间要重置、子进程的 pid、ppid 以及兄弟的要重置。其中上面的 PCB、地址空间、页表都在内核里由操作系统维护的,这也就意味着我们只需要调用操作系统提供的接口 fork,而具体工作细节由操作系统完成。
那其中 为什么 fork() 给父进程返回 子进程的pid,给子进程返回 0 呢???因为我们知道父进程和子进程的关系就是一对多的关系,每个子进程只能有一个孩子,而每个父进程可以有多个子进程,这个 返回值的意义就是为了标识它们的关系!
我们还要知道 fork() 函数是在用户空间中被我们调用的,但是其实现是在内核空间中由操作系统实现的!
fork() 的实现思路,大致如下:
1、给子进程分配新的内存块和内核数据结构(PCB、进程地址空间、页表等,并构建对应的映射关系);
2、将父进程的部分数据结构内容拷贝至父进程;
3、把子进程添加到系统进程列表中;
4、fork 返回,调度器开始调度。

从上图我们知道 既然在 fork 函数 return 之前,就已经有了父子两个进程,父子两个执行流分别执行,所以会给父进程返回子进程的PID,给子进程返回0,失败则返回-1。
让我们看看下面的代码程序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{ pid_t pid; printf("Before: pid is %d\n", getpid()); if((pid = fork()) == -1) // 判断是否返回的是失败值{perror("fork()");exit(1);}printf("After:pid is %d, fork return %d\n", getpid(), pid); sleep(1); return 0;
} 调用结果:
[liren@VM-8-2-centos process]$ ./mypro
Before: pid is 20052
After:pid is 20052, fork return 20053
After:pid is 20053, fork return 0
从上述结果可以看到原来只有父进程一个也就是 20052,但是 fork 之后又产生了子进程 20053!
🔴 注意,fork 之后,谁先执行完全由调度器决定。
调度器是CPU中央处理器的管理员,主要负责完成做两件事情:
选择某些就绪进程来执行
打断某些执行的进程让它们变为就绪状态。
利用 fork 返回值的这个特性,我们可以用变量 id 接收返回值,根据 fork 返回值不同让父子进程执行不同的代码,这个我们之前也讲过啦,简单过一下就好!
#include <stdio.h>
#include <unistd.h>
int grobal_val = 100;
int main()
{pid_t id = fork();if(id == 0){printf("子进程:pid = %d,ppid = %d | grobal_val = %d, &grobal_val = %p\n",getpid(), getppid(), grobal_val, &grobal_val);}else if(id > 0){printf("父进程:pid = %d,ppid = %d | grobal_val = %d, &grobal_val = %p\n", getpid(), getppid(), grobal_val, &grobal_val);sleep(1);}else {printf("fork error\n");return 1;}return 0;
}调用结果:
[liren@VM-8-2-centos process]$ ./mypro
父进程:pid = 18199,ppid = 16903 | grobal_val = 100, &grobal_val = 0x60105c
子进程:pid = 18200,ppid = 18199 | grobal_val = 100, &grobal_val = 0x60105c
Ⅱ.fork的常规用法
- 一个父进程希望复制自己,使得 父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
- 让一个进程执行一个不同的程序。例如子进程从
fork返回后,调用exec函数。(这个会在进程替换中学习)
Ⅲ. fork调用失败的原因
fork 是操作系统级别的接口,所以失败的原因一定是系统级别的原因。
- 系统中已经存在太多的进程了。
- 实际用户创建的进程超过了限制。
这段代码是测试你的用户能跑好多个进程,但是不建议跑。因为跑了之后就会影响 bash,会导致系统出错!代码如下:
#include <stdio.h>
#include <unistd.h>
int main()
{int cnt = 0;while(1){int ret = fork();if(ret < 0){printf("fork error!, cnt: %d\n", cnt);break;}else if(ret == 0){// 子进程不断循环while(1) {printf("子进程:pid = %d,ppid = %d\n",getpid(), getppid());sleep(1);}}// 父进程不断循环不断产生子进程cnt++;}return 0;
}调用结果:
子进程:pid = 1706,ppid = 27353
子进程:pid = 32351,ppid = 27353
子进程:pid = 32348,ppid = 27353
子进程:pid = 32347,ppid = 27353
子进程:pid = 32331,ppid = 27353
............
^C
[liren@VM-8-2-centos process]$
解决方法:
-
用命令
kill -9 -1将进程全部杀死 -
重新增加一个用户使用
Ⅳ. 写时拷贝
当父子代码只读时,父子的代码和数据是共享的。但是任意一方试图写入时,便以写时拷贝的方式各自一份副本。
写时拷贝是一种机制或者策略,好比打仗时的敌退我打,敌进我撤,它根据实时情况来完成既定规则。同理写时拷贝是根据父和子谁先写入的实时情况来完成拷贝的,它是一种延时操作的策略。
具体步骤其实我们在讲进程地址空间的时候已经讲过了,细节见下图:

这里要强调的是这里的写时拷贝是针对数据的写时拷贝,这里留一个疑问 ~~ 代码会发生类似的写时拷贝的问题吗?答案是会的,后面我们讲进程程序替换时候会讲到!
为什么存在写时拷贝❓❓❓
- 写时拷贝是为了保证父子进程的独立性。
- 节省内存和系统资源,提高
fork的效率,减少fork失败的概率。
父子进程创建时,所有数据直接各自拷贝一份不行吗 ???很明显,不使用写时拷贝也可以保证父子进程的独立性,为啥还要费劲使用写时拷贝。其根本原因是:
- 所有的数据,父进程和子进程并不是都必须写入数据,有可能它们仅仅需要读取,而此时的各自拷贝是没有意义的,而且会浪费内存和系统资源。
fork时,创建数据结构,如果还要将数据拷贝一份,那么fork的效率一定会降低。fork本质就是向系统申请更多的内存资源,资源申请多了,fork有可能就会失败。

相关文章:
【Linux系统编程】五、进程创建 -- fork()
文章目录 前言Ⅰ. 重温fork函数一、fork()的概念二、如何理解fork()有两个返回值 Ⅱ.fork的常规用法Ⅲ. fork调用失败的原因Ⅳ. 写时拷贝为什么存在写时拷贝❓❓❓ 前言 现阶段我们知道进程创建有如下两种方式,其实包括在以后的学习中这两种方式也是最常见的&#…...
深入解析 STM32 GPIO:结构、配置与应用实践
理解 GPIO 的工作原理和配置方法是掌握 STM32 开发的基础,后续的外设(如定时器、ADC、通信接口)都依赖于 GPIO 的正确配置。 目录 一、GPIO 的基本概念 二、GPIO 的主要功能 三、GPIO 的内部结构 四、GPIO 的工作模式 1. 输入模式 2. 输出模式 3. 复用功能模式 4. 模…...
深入探究 C++17 std::is_invocable
文章目录 一、引言二、std::is_invocable 概述代码示例输出结果 三、std::is_invocable 的工作原理简化实现示例 四、std::is_invocable 的相关变体1. std::is_invocable_r2. std::is_nothrow_invocable 和 std::is_nothrow_invocable_r 五、使用场景1. 模板元编程2. 泛型算法 …...
Vmware网络模式
一、Vmware虚拟网络 Vmware共支持创建20个虚拟网络,相当于现实生活的交换机,名称vmnet0-vmnet19 没创建一个虚拟网络。对应在物理机会自动生成相应的虚拟网卡 该虚拟网卡用于和对应的虚拟网络中的虚拟机通信 二、虚拟网络的工作模式 1、nat模式 …...
神经辐射场(NeRF):从2D图像到3D场景的革命性重建
神经辐射场(NeRF):从2D图像到3D场景的革命性重建 引言 在计算机视觉和图形学领域,如何从有限的2D图像中高效且准确地重建真实的3D场景,一直是一个重要的研究方向。传统的3D重建方法,如多视角几何、点云重建…...
深入解析AI技术原理
序言 在当今数字化时代,人工智能(AI)已经成为科技领域最炙手可热的话题之一。从智能家居到自动驾驶汽车,从医疗诊断到金融风险预测,AI的应用无处不在。然而,对于许多人来说,AI背后的技术原理仍然充满了神秘色彩。本文将深入探讨AI的核心技术原理,从基础理论到前…...
PDF 2.0 的新特性
近来闲来无事,就想着把PDF的新标准研究研究,略有所得,和大家分享一下。 PDF 2.0的主要新特性包括更高级的加密算法、改进的数字签名和权限管理机制、增强了对非罗马字符的支持,以及扩展了标签架构和3D建模语言“PRC”的支…...
Matlab机械手碰撞检测应用
本文包含三个部分: Matlab碰撞检测的实现URDF文件的制作机械手STL文件添加夹爪 一.Matlab碰撞检测的实现 首先上代码 %% 检测在结构环境中机器人是否与物体之间发生碰撞情况,如何避免? % https://www.mathworks.com/help/robotics/ug/che…...
(root) Additional property include:is not allowed
参考:执行docker compose命令出现 Additional property include is not allowed_(root) additional property include is not allowed-CSDN博客 原因是docker-compose的版本太低,下载最新的替换即可。 第一次2.6.x版本改成了2.19.x不够高,所…...
react 18父子组件通信
在React 18中,父子组件之间的通信方式与之前的版本基本相同,主要可以通过以下几种方式实现: 1. Props(属性) 父组件向子组件传递数据: 父组件通过属性(props)向子组件传递数据&am…...
FastReport 加载Load(Stream) 模板内包含换行符不能展示
如下代码 当以FastReport 载入streams时 当模板内包含换行符时会导致不能正常生成pdf System.Xml.XmlDocument newFrxXml new System.Xml.XmlDocument(); newFrxXml.Load(fileName);FastReport.Report report new FastReport.Report();using (var memStream new MemoryStre…...
Maven 中常用的 scope 类型及其解析
在 Maven 中,scope 属性用于指定依赖项的可见性及其在构建生命周期中的用途。不同的 scope 类型能够影响依赖项的编译和运行阶段。以下是 Maven 中常用的 scope 类型及其解析: compile(默认值): 这是默认的作用域。如果…...
vue3:点击子组件进行父子通信
问: 子组件怎么和爷爷组件通信 回答: 在Vue 3中,子组件和爷爷组件之间的通信可以通过事件冒泡和状态管理来实现。你可以使用Vue的事件系统来传递事件,或者使用全局状态管理库如Vuex或Pinia。以下是一个使用事件冒泡的示例&…...
Composo:企业级AI应用的质量守门员
在当今快速发展的科技世界中,人工智能(AI)的应用已渗透到各行各业。然而,随着AI技术的普及,如何确保其可靠性和一致性成为了企业面临的一大挑战。Composo作为一家致力于为企业提供精准AI评估服务的初创公司,通过无代码和API双模式,帮助企业监测大型语言模型(LLM)驱动的…...
Jackson扁平化处理对象
POJO对象 Data public class People {private PeopleInfo peopleInfo;private List<String> peopleIds;private Map<String, String> peopleMap;Datapublic static class PeopleInfo {private String name;private String address;} }JSON序列化处理 直接将对象进…...
Java即时编译器(JIT)的原理及在美团的实践经验
基本功 | Java即时编译器原理解析及实践 - 美团技术团队 这篇文章由美团AI平台/搜索与NLP部的珩智、昊天、薛超撰写,深入介绍了Java即时编译器(JIT)的原理及在美团的实践经验。 Java执行过程与即时编译器概述 Java执行过程:Java…...
使用 Ollama 在 Windows 环境部署 DeepSeek 大模型实战指南
文章目录 前言Ollama核心特性 实战步骤安装 Ollama验证安装结果部署 DeepSeek 模型拉取模型启动模型 交互体验命令行对话调用 REST API 总结个人简介 前言 近年来,大语言模型(LLM)的应用逐渐成为技术热点,而 DeepSeek 作为国产开…...
算法基础之八大排序
文章目录 概要1. 冒泡排序(Bubble Sort)2. 选择排序(Selection Sort)3. 插入排序(Insertion Sort)4. 希尔排序(Shell Sort)5. 归并排序(Merge Sort)6. 快速排…...
使用TensorFlow和Keras构建卷积神经网络:图像分类实战指南
使用TensorFlow和Keras构建卷积神经网络:图像分类实战指南 一、前言:为什么选择CNN进行图像分类? 在人工智能领域,图像分类是计算机视觉的基础任务。传统的机器学习方法需要人工设计特征提取器,而深度学习通过卷积神经…...
音频进阶学习十一——离散傅里叶级数DFS
文章目录 前言一、傅里叶级数1.定义2.周期信号序列3.表达式DFSIDFS参数含义 4.DFS公式解析1)右边解析 T T T、 f f f、 ω \omega ω的关系求和公式N的释义求和公式K的释义 e j ( − 2 π k n N ) e^{j(\frac{-2\pi kn}{N})} ej(N−2πkn)的释义 ∑ n 0 N − 1 e…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
