【Linux】进程3——PID/PPID,父进程,子进程

在讲父子进程之前,我们接着上面那篇继续讲

1.查看进程

mycode.c

makefile

我们在zs_108直接编译mycode.c,直接运行,然后我们转换另一个账号来查看这个进程

我们可以通过ps指令来查看进程 

 我们就会好奇了,第二行是什么?我们查的是第一行的啊

那个是指令的ps的进程

PID有什么用呢?

一个PID只对应一个进程

这个非常有用,比如说我们可以让一个进程强制停止下来

我们执行下面这个就能让这个进程停止

 程序就被强制停止了

2.通过系统调用获取进程标示符 

2.1.PID——子进程

 我们怎么获得自己的PID呢?

用系统调用——getpid函数

我们用man查一下

我们来使用一下就知道了

mycode.c

makefile 

在执行之前我们先执行这个

注意test是我的可执行程序的名字 

 我们再运行我们编译好的可执行程序

发现右边的也开始出现数据了

发现跟我们查的PID是一样的啊

 我们可以多跑几次,发现PID变了,但是两边的PID都是一样的

2.2.PPID——父进程

PPID是通过函数getppid来实现的

我们还是用man手册查询一下

我们去修改代码

我们重复上面PID的步骤

 

我们再执行我们的test

 完全对的上

我们终止一下我们的程序,我们再打开试试看

我们发现我们的PID变化了,但是父进程PPID没变

那么这个PPID是谁啊

bash进程???

我们每次登录时,系统都会为我们创建一个bash进程

3.通过系统调用创建进程-fork初识

我们可以用man查看fork一下

他的作用是创建一个子进程 

它的用法有的奇怪,我来展示一下

我们运行一下这段代码

我们再修改一下代码

再执行一下

怎么还打印到命令行里面来了???

 我们再修改一下代码

 

before是一条,after是两条,为什么呢?

这个是因为fork()的原因

按照手册的说法,fork的返回值既大于0又等于0,这可能吗?????

我们来验证一下

 我们编译运行一下

两个死循环在跑???

这个在c语言是基本不可能的,但是这个在操作系统是可以的

 有没有发现children的PPID和parent的PID是一样的,所以他们是纯正的父子关系

在执行fork之前只有一个进程,执行到fork后就创建子进程,父进程就是原来的那个进程

 创建进程的方式

  1. ./可执行程序
  2. fork()函数

 接下来我们好好来好好研究一下fork函数

程序开始时按照从上往下的执行顺序创建一个执行流,执行到fork时创建子进程,子进程的返回值等于0,父进程的返回值大于0 ,就变成了两个执行流,父进程是旧的那个执行流,子进程是新创建的进程

3.1 为什么要创建子进程?

 父进程创建子进程通常是希望子进程来协助父进程来完成一些工作,这些工作是单进程无法实现的。

 例如:用户在下载某一款软件时,同时存在播放该款软件的官方宣传视频的需求。这就需要子进程来协助父进程来达到边下载边播放的目的。即通过一定手段(通常时fork()的返回值),让父子进程分别执行不同的代码!

3.2 fork()究竟干了些什么?

  1. fork()创建进程后,OS中会多一个进程(子进程)。
  2. 在创建过程中,Linux操作系统会为子进程创建对应的PCB,并用父进程的大部分属性来初始化子进程的相关属性(如子进程的pid、ppid、所在路径等属性信息则是单独实现)。
  3. 最后将该进程链入到运行队列中,等待CPU的调度!!

而进程 = 代码 + 内核数据结构和数据,并且进程间时是相互独立的。而进程中的代码和数据等是操作系统在创建该进程时,从磁盘上加载拷贝到内存中的。

但创建的子进程是直接在内存中创建的,子进程并没有相应的代码和数据,那要怎么解决这个问题呢?

  1. 实际上,代码只是用于读取的。
  2. 所以Linux中fork()创建的子进程和父进程共用同一段代码。
  3. 但对于数据来说,父/子进程间必然会存在差异(比如pid、ppid等)。
  4. 同时为了保证父/子进程间的独立性,必须在父/子进程中各自独立私有一份。
  5. 而在Linux中采用了写时拷贝的方式来解决这个问题。

下一个问题就是为啥我们在前面的展示中,fork()创建出的子进程只执行fork()后的代码?在fork()前的代码子进程能否“看见”呢?

  1. 答案是子进程能看见fork()前的所有代码!
  2. 至于为啥子进程只执行fork()后的代码,这是由于代码运行过程中,存在诸如epi、pc等寄存器。
  3. 这些寄存器中会保存当前指令要执行的下一条指令的地址。
  4. 而fork()创建子进程过程中,父进程中pc、epi等寄存器的结果也“继承”给了子进程。
  5. 所以才出现子进程只运行执行fork()函数后的代码!

3.3 fork为什么会存在两个返回值?

        理解一个函数如何被返回两次,尤其是fork(),首先需要深入了解fork()的工作原理。

        在没有调用fork()之前,系统中只有一个进程。进程由内核数据结构和与之关联的代码和数据组成。当创建一个新进程时,需要为它创建相应的进程控制块(PCB)以及与之关联的代码和数据。CPU随后会调度这个新进程,执行它的代码和数据。

        调用fork()后,会创建一个新的子进程。创建子进程的本质是系统中多了一个新进程。由于进程由内核数据结构和代码及数据组成,因此新创建的子进程首先需要创建自己的task_struct(Linux内核中的进程描述符)。这个子进程的大部分属性是基于父进程的属性创建的,相当于复制了父进程的对象并对部分属性进行了修改。例如,子进程会有自己的PID,而其父进程ID(PPID)则设置为父进程的PID。这样,父子进程就有了自己的ID关系。

        然而,子进程在刚创建时并没有自己的代码和数据,因此它只能访问父进程的代码和数据。这就是为什么fork()之后父子进程的代码是共享的。当CPU调度并运行父进程的代码时,它执行的是父进程的代码;当调度并运行子进程的代码时,它执行的仍然是父进程的代码

3.4 为何fork函数中父进程返回子进程的pid、子进程返回0?

        一般而言啊,fork之后的代码父子共享啊,也就是说呢,其实当我们fork之后,后续的所有代码父子进父子进程他都能看到啊,只不过呢,我们是需要通过一定程度去区分,我们可以让父进程和子进程执行不同的代码块,那么当然应该这样,要不然我为什么创建子进程呢,我不就是想让你的子进程过来帮我忙嘛,把你创建出来,让你和我做不同的事情,所以我们的返回值一定是需要不同的,即便是返回值未来,如果假设系统设计者把它设成相同的了,未来我们一定也要有方法区分父子进程,这是这个,当然这不是这个问题的答案,问题是为什么要给子进程反回0给父进程反回pid,上面回答的是为什么父子进程返回值不同,我们现在知道了,因为父子进程,那么后续的代码是共享的,而我们可以通过不同的返回值来区分不同的执行流,让父子执行不同的代码块好也没有毛病,具体他执行的代码块要干啥我们后面来介绍啊 现在的问题是为什么要给子进程返回0给父进程返回pid。

        给大家举一个生活当中的例子啊:
        在现实生活中,一个父亲可以有多个子女,而每个子女只有一个父亲,这是容易理解的。在进程的世界中,一个父进程也可能有多个子进程。但有时,我们可能需要对特定的子进程进行控制。想象一下,如果一个家庭有10个孩子,当父亲喊“儿子,你过来”时,所有10个孩子都过来了,那么父亲到底是想叫哪个孩子呢?
        因此,对于父进程来说,它必须有一种方法来区分每一个子进程。这就是为什么fork()在父进程中返回子进程的PID。这样,父进程就可以使用这个PID来标定和识别每一个子进程的唯一性。而对于子进程来说,情况就简单了。它只需要调用getppid()函数就可以直接获取其父进程的PID。所以,子进程要找到父进程并不需要花费太多成本,它只需要通过返回0来表示成功就可以了。这就是为什么我们在父进程中返回子进程的PID的原因,因为未来我们可能想通过父进程使用PID来明确控制我们要访问的是哪一个子进程。

3.5 父进程和子进程谁先运行?

 我们已经看到了fork()函数会创建一个子进程。创建完子进程后,子进程会被加载链接到系统进程列表中等待CPU调度运行。
 至于父进程和子进程谁先被CPU调度是不确定的。进程被调度的先后顺序由各自的PCB中的调度信息(时间片 + 优先级等)+ 调度器算法确定。换句话说,父进程和子进程谁先运行是由操作系统决定的!

3.6 为何同一个变量接收两个返回值

        当使用fork()创建子进程时,系统中确实多了一个进程,这也意味着操作系统内必须为这个新进程创建一个对应的PCB(进程控制块)。这个子进程的PCB同样可以被CPU调度运行。与父进程不同,子进程在创建之初并没有自己的代码和数据。

        需要明确的是,进程在运行时具有独立性。这意味着一个进程的崩溃或退出不会影响到其他进程。例如,如果在Windows上运行的QQ崩溃了,它不会影响到同时运行的画图软件或XShell等其他进程。每个进程都是独立的,它们的崩溃或退出不会影响其他进程。

        为了保证进程的独立性,不能让父进程和子进程共享同一份数据,因为数据可能会被修改,修改后可能会影响其他进程。因此,虽然父子进程的代码是共享的,但数据不一定是共享的。理论上,子进程需要为自己拷贝一份父进程的数据。但实际上,数据的拷贝工作是由操作系统自动完成的。当子进程需要访问父进程的某部分数据时,操作系统会识别出这一需求,并在系统内存中为子进程重新开辟一段空间。子进程在新开辟的空间内进行数据的修改,这样就不会影响到父进程的数据。这种技术被称为写时拷贝(Copy-on-Write)。它确保了父子进程在数据层面的独立性,同时也避免了不必要的数据冗余。

        总结来说,fork()创建的子进程具有独立的PCB,可以被CPU调度运行。虽然父子进程的代码是共享的,但数据不一定是共享的。操作系统通过写时拷贝技术确保父子进程在数据层面的独立性,同时也避免了数据冗余。

        fork 函数在执行 return 返回的时候是往 id 变量里面写入嘛,父进程 return 一次,子进程 return 一次,子进程会执行写时拷贝,这就是为什么一个 id 变量有两个不同的数据,本质上是因为有两块空间。为什么同一个变量名可以有不同的内容,这个问题我们在后面介绍进程地址空间的时候为大家介绍。

我们来深入了解一下

  • 操作系统在创建子进程时,会先将父进程页表中的访问权限大部分改为只读权限。
  • 然后以父进程为模板,将数据拷贝给子进程。
  • 当子进程或父进程对数据试图修改数据,由于页表中的访问权限为只读。此时该行为会被操作系统识别到。并分为以下两种情况:
  1. 如果代码和数据所处区域本身就是只读的(如:代码区、字符常量区),此时OS认为该行为非法被拦截。
  2. 原本数据可读可写,但操作系统将对应页表中的访问权限设置为只读。当操作系统识别到该信息时,操作系统并不会将行为视为错误,而是作为重新申请内存拷贝内容的一种策略机制。此时操作系统会重新申请开辟空间,拷贝和修改数据,并修改页表中的映射关系。

 3.7.fork创建子进程失败原因

在Linux中,fork()创建子进程失败通常由有以下两种原因:

  1. 系统中有太多的进程。操作系统内存不足。
  2. 实际上,用户使用fork()创建子进程是有次数限制的。当超过该范围时,fork创建子进程失败!

3.8.bash如何创建子进程?

我们看看程序最开始的那个printf,PPID是45106,那么45106是谁呢?

很明显了,就是我们自己的PID

这个bash创建子进程的过程肯定也调用了fork()

我们接着从这个故事说起:

        “王婆为了保证自己在给别人说媒时,如果失败了,不会影响到自己,她是通过创建子进程来实现的。现在我的问题是,bash是如何创建子进程的?虽然我们还没有看或写bash的源代码,但我可以推断一下这个过程。

        bash一定会调用fork来创建子进程,并让自己的代码继续执行命令行解释,而让子进程去执行解释新的命令。那么,bash自己就会继续接受用户的输入,并打印出提示符,等待用户输入新的命令。这就是为什么我们运行的所有命令都是在bash内部,因为所有的进程都是通过fork创建出来的。

        当然,这只是问题的一半,后面还有更多内容需要探讨。但我们已经理解了创建子进程的过程,核心结论就是bash也是一个进程,当执行命令时,bash内部一定会为我们fork创建新的子进程,然后执行我们的代码。”

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

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

相关文章

Linux shell编程学习笔记58:cat /proc/mem 获取系统内存信息

0 前言 在开展系统安全检查的过程中,除了收集cpu信息,我们还需要收集内存信息。在Linux中,获取内存信息的命令很多,这里我们着重研究 cat /proc/mem命令。 1 cat /proc/mem命令 /proc/meminfo 文件提供了有关系统内存的使用情况…

280 基于matlab的摇号系统GUI界面仿真MATLAB程序

基于matlab的摇号系统GUI界面仿真MATLAB程序,输入总数量及摇号需求,进行随机性摇号,并对摇取的号码进行双重随机性数据检测,确定是否符合要求。程序已调通,可直接运行。 280 GUI人机交互 摇号系统GUI界面仿真 - 小红书…

技术前沿 |【大模型InstructBLIP进行指令微调】

大模型InstructBLIP进行指令微调 一、引言二、InstructBLIP模型介绍三、指令微调训练通用视觉语言模型的应用潜力四、InstructBLIP的指令微调训练步骤五、实验结果与讨论六、结论与展望 一、引言 随着人工智能技术的快速发展,视觉语言模型(Vision-Langu…

使用SourceTree切换不同的托管平台

背景:sourcetree一开始绑定了gitee,想拉取github的项目时拉取不了 原因:git绑定的账号(邮箱)、密码不一致 解决办法: 重新设置账号密码 在windows种可找到下面的文件夹,进行删除 C:\Users\US…

5.1 实体完整性

一个表只能有一个主键约束,且主键约束不能取空值。 通过unique约束定义唯一性,为了保证一个表非主键列不输入重复值,可在该列定义unique约束。 primary key约束与unique约束主要区别如下。 (1)一个表只能创建一个primary key约束&#xff0…

让GNSSRTK不再难【第一天】

第1讲 GNSS系统组成以及应用 北斗导航科普动画_哔哩哔哩_bilibili 1.1 GNSS系统 1.1.1 基本概念 全球卫星导航系统(Global Navigation Satellite System, GNSS),是能在地球表面或近地空间的任何地点为用户提供全天候的三维坐标、速度以及…

STM32-电灯,仿真

目录 前言: 一. 配置vscode 二. 新创建软件工程 三. 仿真 1.新建工程想到,选择名称和路径 2.从选中的模板创建原理图 3.不创建PCB布版设计 4.选择没有固件项目 5.完成 四.源码 五. 运行效果 六. 总结 前言: 这篇主要是配置vscode和创建仿真,和点灯的完整代码,欢迎大…

在Windows上用Llama Factory微调Llama 3的基本操作

这篇博客参考了一些文章,例如:教程:利用LLaMA_Factory微调llama3:8b大模型_llama3模型微调保存-CSDN博客 也可以参考Llama Factory的Readme:GitHub - hiyouga/LLaMA-Factory: Unify Efficient Fine-Tuning of 100 LLMsUnify Effi…

美琳莱卡:创新消费模式引领新零售时代

公司成立时间与定位 美琳莱卡自创立之初,便以独特的视角和前瞻性的战略定位,立足于消费市场的变革前沿。公司成立于2024年,正值全球数字化浪潮蓬勃兴起,消费升级趋势日益明显之际。美琳莱卡敏锐地捕捉到这一时代机遇,将自身定位为创新消费模式的引领者,致力于通过线上线下高度…

攻防演练之-网络集结号

每一次的网络安全攻防演练都是各个安全厂商期待的网络安全盛会,因为目前的安全生态导致了只有在网络安全攻防演练期间,网络安全的价值才会走向台前,收到相关方的重视。虽然每一次都会由于各种原因不能如期举行,但是这一次的推迟总…

idea最新专业版安装+maven配置教程!

本教程适用于 J B 全系列产品,包括 Pycharm、IDEA、WebStorm、Phpstorm、Datagrip、RubyMine、CLion、AppCode 等。 (直接复制,拿走不谢) 9H1390TRAK-eyJsaWNlbnNlSWQiOiI5SDEzOTBUUkFLIiwibGljZW5zZWVOYW1lIjoi5rC45LmF5rA5rS7I…

MySQL之查询性能优化(七)

查询性能优化 排序优化 无论如何排序都是一个成本很高的操作,所以从性能角度考虑,应尽可能避免排序或者尽可能避免对大量数据进行排序。前面已经提到了,当不能使用索引生成排序结果的时候,MySQL需要自己进行排序,如果…

【传知代码】上下位关系自动检测方法(论文复现)

前言:在信息爆炸的时代,我们每天都沉浸在海量的数据和信息中。随着互联网技术的飞速发展,如何从这些信息中准确、高效地提取出有用的知识,成为了当下研究的热点。其中,上下位关系(也称为层级关系或种属关系…

vscode 中 eslint 无效?npm init 是什么?

vscode 中 eslint 无效 我想要给一个项目添加 eslint,按照 eslint 官方指南操作: npm init eslint/configlatest自动安装了相关依赖并创建配置文件 eslint.config.mjs。 按理说,此刻项目应该已经配置好 eslint 了。但是我的编辑器 vscode …

《python程序语言设计》2018版第5章第36题改造4.17 石头 剪刀 布某一方超过2次就结束。

代码编写记录 2024.05.04 05.36.01version 换一个什么数代替剪子 我先建立一个函数judgement condition 石头3 剪子2 布1 如何构建一个循环进行的架构,是我们最需要的想法 循环以什么条件开始呢 是小于2个还是大于2个。 guess_num random.randint(1, 3) computer…

Python 和 Java 实现云计算的最终年项目

1、问题背景 目前,我正在进行我的最终年项目,计划用 Python 编写一个云计算系统,而云客户端将由我的团队成员使用 Java 来编写。这个云客户端将具有一个带有标签的界面,并提供文本编辑器、媒体播放器、几个基于 Java 的小游戏以及…

按键精灵在Win11中弹窗出现乱码并且自带的部分系统插件不能使用的解决方法

按键精灵中出现以下问题: 提示信息的弹窗出现乱码: 系统自带的部分像 plugin. 开头的插件不能使用,如下:s Plugin.Sys.GetDateTime() screenX Plugin.GetSysInfo.GetScreenResolutionX screenY Plugin.GetSysInfo.GetScreenRe…

⌈ 传知代码 ⌋ 记忆大师

💛前情提要💛 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间,对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

IDEA去除代码和XML中的波浪线(黄色警告线)

通常情况下,IDE自带的侦测功能会帮我们提示一些错误、警告等。但这对于强迫症患者来说并不友好。以下是去除IDE代码和XML文件中的波浪线(黄色警告线)、拯救强迫症患者的有效方案 1、去除XML中的波浪线 2、去除代码中的波浪线 关爱强迫症患者…

VUE2.7项目配置webpack打包-详细操作步骤

一、Webpack简介 Webpack是一个打包工具,可以把JS、CSS、Node Module、Coffeescrip、SCSS/LESS、图片等都打包在一起,因此,现在几乎所有的SPA项目、JS项目都会用到Webpack。 官网:https://webpack.js.org GitHub为https://git…