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

【Linux系统编程】如何创建进程(什么是fork函数?进程创建的原理是什么?)

目录

一、前言

二、 进程创建的初次了解(创建进程的原理)

三、什么是fork函数? 

 💦初识fork函数

 💦fork函数的四个为什么? 

⭐为什么fork()要给子进程返回0,给父进程返回子进程pid?

⭐一个函数是如何做到返回两次的?如何理解?

⭐fork()函数究竟在干什么?干了什么?

⭐ 一个变量怎么会有不同的内容呢?

四、总结 

五、共勉 


一、前言

        在之前的博客中,已经详细的讲解了什么是进程包括了进程的概念进程与操作系统的关系如何查看进程等。

        所以本次博客将详细讲解,如何创建一个进程,让大家更加深入的了解进程!!

二、 进程创建的初次了解(创建进程的原理)

创建新进程Linux的下是由父进程来完成的,创建完成的新进程子进程。
新进程的地址空间有两种可能性:

  1. 子进程是父进程的复制品(除了PID和task_struct是子进程自己的,其余的都从父进程复制而来)
  2. 子进程装入另一个程序。

在Linux下的fork函数用于创建一个新的进程使用fork函数来创建一个进程时,子进程只是完全复制父进程的资源。这样得到的子进程和父进程是独立的,具有良好的并发性。但是进程间通信需要专门的机制。

三、什么是fork函数? 

之前我们在Linux下启动一个进程的时候利用的是./可执行程序那是否有其他办法去启动一个进程呢? 

 💦初识fork函数

当然是有的,那就是使用fork()这个函数。在使用之前呢我们要先去查看一下这个函数该如何使用------ 使用man 手册查询一下 fork 函数的使用

man 2 fork
  • 可以看到,这个函数的功能就是去创建一个子进程,其返回值为pid_t
  • 注意:这里的 pid_t 类型 是无符号整数

 函数说明:

  • 通过复制调用进程创建一个新进程。

  • fork 有两个返回值。

  • 父子进程代码共享,数据各自私有一份(采用写时拷贝)。

接下来,我们来测试一段代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>    // getpid, getppid, fork, sleep
#include <sys/types.h>  // getpid, getppidint main()
{printf("before: I am a process\n");fork();printf("after: 创建一个新进程\n");return 0;
}
  • 通过执行结果我们可以看到,虽然只有一句after: 创建一个新进程但是在【fork】之后却打印了两句

 💬 那有的同学就会感到非常地好奇,这是为什么呢?

        因为在【fork】之后会产生两个执行的进程。但有同学还是会觉得很怪,这怎么就变成了两个进程了呢?我们可以去查询一下这个单词的意思,发现其确实是有分叉的意思。所以在执行了这个函数后,就会存在两个执行流

如果想要更加清楚地了解这个函数,我们还需要再查看一下man手册,然后看到

  • 如果成功则会给父进程返回子进程的PID,给子进程返回0
  • 如果失败的话则会给父进程返回-1


        当我们知道原理后,在代码中输出进程PID,看是否和我们的fork理解一样,让我们一起来验证一下吧!

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>    // getpid, getppid, fork, sleep
#include <sys/types.h> // getpid, getppid 
int main()
{id_t id = getpid();printf("before: I am a process,我的进程PID为:%d\n",id);fork();printf("after: 创建一个新进程,我的进程PID为:%d, 我的父进程PPID为:%d\n",getpid(),getppid());       return 0;
}

 那接下去我们就根据这个返回值去举个例子看看

下面是测试的代码: 

int main()
{printf("fork之前:I am a process,pid: %d, ppid: %d\n",getpid(),getppid());sleep(5);printf("开始创建进程啦!\n");sleep(1);pid_t id = fork();if(id < 0){return 1;}else if(id == 0){while(1){// 子进程  printf("fork之后, 我是子进程:I am a process,pid: %d, ppid: %d,return id: %d\n",getpid(),getp    pid(),id);                                                                                         sleep(1);}}else{while(1){// 父进程printf("fork之后,我是父进程:I am a process,pid: %d, ppid: %d,return id: %d\n",getpid(),getp    pid(),id);sleep(1);}}sleep(2);return 0;}
  • 然后将进程挂起后我们来看看,在第一句执行完后父子进程竟然是一起执行的,if...else...分支可以同时进去,并且还有两个死循环在同时跑。这是为什么呢?

分析:

  • 我们来分析一下这个进程的创建过程:首先我们可以看到我们在这个PPID为【24609】 bash 上执行了一个进程,那么操作系统就会为这个进程分配一个PID为【12484】
  • 接下去这个进程被操作系统调度,执行自己的代码,执行到内部代码的fork函数时,执行流被一分为二,变成了两个执行分支:一个是父进程(它自己),另一个则是子进程(新的分支)

所以现在我们可以得出创建进程的两种方式:

  1. ./运行我们的程序 - - 指令层面---------(bash)
  2. fork() - - 代码层面

💦fork函数的四个为什么? 

 ⭐为什么fork()要给子进程返回0,给父进程返回子进程pid

       上面我们说到当进程的代码执行到fork()函数的时候,会将执行流一分为二,父子进程通过不同的 id 返回值来区分,以此执行不同的代码块。那其实很好理解了:因为父子进程是两个不同的进程,所以需要根据这个不同的返回值来进程区别



💬 那有同学说:你这不说了跟没说一样嘛,要区分的话当然得不同了,那为什么父进程得到的是子进程的PID,但是子进程却是0呢,为什么不可以倒过来?

  • 这位同学,你问到点子上了,确实这是它们最大的区别,不过呢这样的返回值还是有原因的。读者可以这么来理解:一个父亲可以有多个孩子👦,但是呢一个孩子却只能有一个父亲👨 父亲所获取到的返回值是子进程的PID是由于他要靠不同的PID值来区分不同的孩子;但子进程的返回值都是0的原因在于他一定只对应着某一个父进程,只需让父进程知道它被成功创建出来了即可

 ⭐一个函数是如何做到返回两次的?如何理解?

上面讲到了因为在某些情况下需要依靠父子进程去执行不同的两段逻辑,所以在创建子进程后父子进程它们分别会得到不同的两个值

  • 那既然在调用了fork()函数后,就肯定需要去返回两次才可以。这里我们再通过画图来分析一下,既然这个fork()是库函数的话,那执行到这一句的时候就一定会跳转到库中的这一逻辑中去执行【创建子进程】的这一步的步骤,但是这还是无法说明他可是有不同的返回值呀?
  • 那我这时候就要问了,最后的这个return语句算是代码吗? 当然了!那我们在上面说到过这个代码呢是父子进程共享的,那么父进程返回一次,子进程返回一次,也就相当于返回了两次
     

 

fork()函数究竟在干什么?干了什么?

         在上面我们讲到过【进程 = 内核数据结构 + 代码与数据】,当我们在执行完fork()函数后,子进程被创建出来,那么它的PCB结构体即 task_struct 会被构建出来,我们知道的是在每个进程的结构体中有PID和PPID这两个成员,而且对于子进程中的PPID恰好就是父进程中的PID所以子进程大部分的属性就是以父进程为模版创建的,相当于把父进程拷贝了一份,对部分属性做了修改


       可以看出子进程被创建出来后系统中多了一个进程,那么对于父进程来说它有自己的内核数据结构、代码和数据,子进程也按照父进程的PCB模拟了一块出来
 

💬 那我现在要问了:请问子进程的数据和代码呢?也是拷贝出来的吗? 

  • 那有的同学就说:都属于不同的两个进程了,总会有自己的代码和数据吧。诶,这个说得就不对了,对于子进程来说,虽然它有自己的内核数据结构,但它在一创建出来的时候并没有独属于自己的【代码和数据】,而也是使用和父进程一样的同一份代码和数据


 

 ⭐一个变量怎么会有不同的内容呢?

在我们上面的代码中,我们会发现 id 这个变量会有两个不同的值?这是为什么呢?

 因为父子进程使用的是不同的PCB,所以当CPU调度不同进程访问的是不同的数据,它们在数据上割裂了,那一个进程运行时就不会影响另一个进程了。也就是双方虽然共享数据,但是不能对数据进行修改,若要是想对数据进行修改的话,就对数据进行拷贝,去修改拷贝出去的那一份。

总结:
当进程里数据没有发生修改时。那么子进程和父进程共享代码和数据!如果有一个进程的数据发送了修改,那么会对数据发送写实拷贝,而代码依旧是共享的
 


 

如果发生修改,那么父进程的代码数据会进行一份实时拷贝,给子进程。

 


 

四、总结 

1.子进程是由父进程的模板创建的
2.子进程和父进程一般情况下共享代码和数据,但如果有一方的数据或代码被修改,那么操作系统会写实拷贝一份。
3.fork函数不是真的有2个返回值,而是两个进程都在fork函数执行了返回操作。
4.fork返回父进程子进程的PID,给子进程返回0,进程创建失败返回-1

五、共勉 

以下就是我对【Linux系统编程】如何创建进程的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对【Linux系统编程】进程状态的理解,请持续关注我哦!!! 

 

相关文章:

【Linux系统编程】如何创建进程(什么是fork函数?进程创建的原理是什么?)

目录 一、前言 二、 进程创建的初次了解&#xff08;创建进程的原理&#xff09; 三、什么是fork函数&#xff1f; &#x1f4a6;初识fork函数 &#x1f4a6;fork函数的四个为什么&#xff1f; ⭐为什么fork()要给子进程返回0&#xff0c;给父进程返回子进程pid&#xff…...

【opencv】计算机视觉基础知识

目录 前言 1、什么是计算机视觉 2、图片处理基础操作 2.1 图片处理&#xff1a;读入图像 2.2 图片处理&#xff1a;显示图像 2.3 图片处理&#xff1a;图像保存 3、图像处理入门基础 3.1 图像成像原理介绍 3.2 图像分类 3.2.1 二值图像 3.2.2灰度图像 3.2.3彩色图像…...

Node——Node.js简介

Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;它能够让JavaScript脚本运行在服务端&#xff0c;这使得JavaScript成为与PHP、Python等服务端语言平起平坐的脚本语言。 1、认识Node.js Node.js是当今网站开发中非常流行的一种技术&#xff0c;它以简单易…...

小型洗衣机什么牌子好又便宜?性价比迷你洗衣机推荐

由于日常所穿的内衣裤由于各种原因&#xff0c;时间一久就很容易产生细菌&#xff0c;而且和其他大件的衣物一起混洗&#xff0c;很容易造成细菌的交叉感染&#xff0c;积攒起来洗就更不卫生了&#xff0c;留在内衣裤上的分泌物会继续滋生细菌&#xff0c;比如闷热的环境下念珠…...

INFINI Easysearch 与华为鲲鹏完成产品兼容互认证

何为华为鲲鹏认证 华为鲲鹏认证是华为云围绕鲲鹏云服务&#xff08;含公有云、私有云、混合云、桌面云&#xff09;推出的一项合作伙伴计划&#xff0c;旨在为构建持续发展、合作共赢的鲲鹏生态圈&#xff0c;通过整合华为的技术、品牌资源&#xff0c;与合作伙伴共享商机和利…...

将linux服务器 设置成 proxy.SOCKS5 服务器

gpt: 如果你想在 Linux 服务器上设置一个 SOCKS5 代理服务器&#xff0c;你可以使用一些现有的工具&#xff0c;比如 Shadowsocks、Dante、或者其他支持 SOCKS5 协议的软件。下面是一个使用 Dante 的简单示例&#xff1a; 1. **安装 Dante&#xff1a;** bash sudo apt-g…...

无mac电脑生成uniapp云打包私钥证书的攻略

uniapp顾名思义是一个跨平台的开发工具&#xff0c;大部分uniapp的开发者&#xff0c;其实并没有mac电脑来开发&#xff0c;但是生成ios的证书&#xff0c;官网的教程却是需要mac电脑的&#xff0c;那么有没有办法无需mac电脑即可生成uniapp云打包的私钥证书呢&#xff1f; 下…...

py 启动默认浏览器

要在Python中启动默认浏览器&#xff0c;可以使用第三方库如webbrowser或pyperclip&#xff0c;也可以使用操作系统特定的命令行调用。以下是几个不同的方法&#xff1a; 使用webbrowser库&#xff1a; import webbrowser webbrowser.open("http://example.com")这…...

scala可变参数列表使用

在Scala中&#xff0c;可以使用可变参数列表&#xff08;varargs&#xff09;来定义一个函数&#xff0c;以接受可变数量的参数。示例如下&#xff1a; def printArgs(args: String*): Unit {args.foreach(println) }// 使用可变参数列表 printArgs("Hello", "…...

经验分享:JMeter控制RPS

一、前言 ​ RPS (Request Per Second)一般用来衡量服务端的吞吐量&#xff0c;相比于并发模式&#xff0c;更适合用来摸底服务端的性能。我们可以通过使用 JMeter 的常数吞吐量定时器来限制每个线程的RPS。对于RPS&#xff0c;我们可以把他理解为我们的TPS&#xff0c;我们就…...

JavaScript中的for循环你用对了吗?

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;JavaScript篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript-for循环 目录 循环结构 循环思想&#xff08;三要素&#xff09; 实现…...

WordPress(10)解决中文连接问题

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、修改的前后二、自定义结构讲明三、修改方法前言 提示:这里可以添加本文要记录的大概内容: 1.中文连接如:http://www.lplovemm.love/2023/11/12/测试 2.这种连接在提交sitemap收录的时…...

2023年小美赛认证杯国际赛A题解题思路+数据分享版+部分代码

小美赛作为一个与美赛高度相似的比赛&#xff0c;通常作为很多队伍的美赛练手赛&#xff0c;本次也将尝试帮助大家对A题进行求解&#xff0c;希望能够对大家有所帮助。真实的选题人数评估预计明天进行公布 难度 B>DC>A 选题 A>B>D>C 问题A&#xff08;MCM&am…...

删除list中除最后一个之外所有的数据

1.你可以新建一个list List<Integer> listnew ArrayList<>();int i0;while (i<100){list.add(i);}List<Integer> subList list.subList(list.size()-1, list.size());System.out.println("原list大小--"list.size());System.out.println("…...

北京筑龙助力中粮集团采购供应链改革加速跑

11月6日&#xff0c;在第六届中国国际进口博览会&#xff08;简称“进博会”&#xff09;上&#xff0c;中粮集团与来自全球的供应商签约进口农产品&#xff0c;金额超百亿美元&#xff0c;签约额、进口量、进口来源地覆盖范围均创历史新高。 这只是中粮集团落实集中采购&…...

SpringBoot学习笔记-实现微服务:匹配系统(中)

笔记内容转载自 AcWing 的 SpringBoot 框架课讲义&#xff0c;课程链接&#xff1a;AcWing SpringBoot 框架课。 CONTENTS 1. 同步玩家位置1.1 游戏信息的记录1.2 实现多线程同步移动 2. 同步碰撞检测3. 实现游戏结束界面4. 持久化游戏状态4.1 创建数据库表4.2 保存游戏对局信息…...

【复杂网络建模】——基于代理的社会网络建模(Agent-Based Modeling,ABM)[Python实现]

目录 一、复杂网络建模方法 二、基于代理的社会网络建模实现及Python实现代码 一、复杂网络建模方法 复杂网络是一种由大量相互连接的元素(节点或顶点)组成的网络结构,这些连接通常是非常复杂和动态的。这些网络可以在各种领域中发现,包括社交网络、生物学系统、信息技术…...

RSA实现中弱密钥漏洞分析(Analyzing Weak Key Vulnerabilities in RSA Implementation)

点我完整下载&#xff1a;《RSA实现中弱密钥漏洞分析》本科毕业论文一万字.doc RSA实现中弱密钥漏洞分析 "Analyzing Weak Key Vulnerabilities in RSA Implementation" 目录 目录 2 摘要 3 关键词 4 第一章 引言 4 1.1 研究背景 4 1.2 研究目的 5 1.3 研究意义 6 第…...

【管理运筹学】背诵手册(六)| 图与网络分析(最大流问题,最小费用最大流问题)

六、图与网络分析 最大流问题 最大流问题的数学规划模型为&#xff1a; max ⁡ v f 12 f 13 { f 12 f 13 − f 57 − f 67 0 f 13 f 23 f 34 f 35 . . . 0 ≤ f i j ≤ c i j \max vf_{12}f_{13}\\ \begin{cases} f_{12}f_{13}-f_{57}-f_{67}0 \\ f_{13}f_{23}f_{34}f…...

C语言之结构体详解

C语言之结构体详解 文章目录 C语言之结构体详解1. 结构体类型的声明2. 结构体变量的创建和初始化3. 结构体的特殊声明4. 结构体的自引用结构体的自引用匿名结构体的自引用 5. 结构体内存对齐5.1 练习一5.2 练习三 6. 为什么存在内存对⻬? 1. 结构体类型的声明 struct tag {me…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

设计模式和设计原则回顾

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

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...