【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后就创建子进程,父进程就是原来的那个进程
创建进程的方式
- ./可执行程序
- fork()函数
接下来我们好好来好好研究一下fork函数

程序开始时按照从上往下的执行顺序创建一个执行流,执行到fork时创建子进程,子进程的返回值等于0,父进程的返回值大于0 ,就变成了两个执行流,父进程是旧的那个执行流,子进程是新创建的进程
3.1 为什么要创建子进程?
父进程创建子进程通常是希望子进程来协助父进程来完成一些工作,这些工作是单进程无法实现的。
例如:用户在下载某一款软件时,同时存在播放该款软件的官方宣传视频的需求。这就需要子进程来协助父进程来达到边下载边播放的目的。即通过一定手段(通常时fork()的返回值),让父子进程分别执行不同的代码!
3.2 fork()究竟干了些什么?
- fork()创建进程后,OS中会多一个进程(子进程)。
- 在创建过程中,Linux操作系统会为子进程创建对应的PCB,并用父进程的大部分属性来初始化子进程的相关属性(如子进程的pid、ppid、所在路径等属性信息则是单独实现)。
- 最后将该进程链入到运行队列中,等待CPU的调度!!
而进程 = 代码 + 内核数据结构和数据,并且进程间时是相互独立的。而进程中的代码和数据等是操作系统在创建该进程时,从磁盘上加载拷贝到内存中的。
但创建的子进程是直接在内存中创建的,子进程并没有相应的代码和数据,那要怎么解决这个问题呢?
- 实际上,代码只是用于读取的。
- 所以Linux中fork()创建的子进程和父进程共用同一段代码。
- 但对于数据来说,父/子进程间必然会存在差异(比如pid、ppid等)。
- 同时为了保证父/子进程间的独立性,必须在父/子进程中各自独立私有一份。
- 而在Linux中采用了写时拷贝的方式来解决这个问题。
下一个问题就是为啥我们在前面的展示中,fork()创建出的子进程只执行fork()后的代码?在fork()前的代码子进程能否“看见”呢?
- 答案是子进程能看见fork()前的所有代码!
- 至于为啥子进程只执行fork()后的代码,这是由于代码运行过程中,存在诸如epi、pc等寄存器。
- 这些寄存器中会保存当前指令要执行的下一条指令的地址。
- 而fork()创建子进程过程中,父进程中pc、epi等寄存器的结果也“继承”给了子进程。
- 所以才出现子进程只运行执行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 变量有两个不同的数据,本质上是因为有两块空间。为什么同一个变量名可以有不同的内容,这个问题我们在后面介绍进程地址空间的时候为大家介绍。
我们来深入了解一下
- 操作系统在创建子进程时,会先将父进程页表中的访问权限大部分改为只读权限。
- 然后以父进程为模板,将数据拷贝给子进程。
- 当子进程或父进程对数据试图修改数据,由于页表中的访问权限为只读。此时该行为会被操作系统识别到。并分为以下两种情况:
- 如果代码和数据所处区域本身就是只读的(如:代码区、字符常量区),此时OS认为该行为非法被拦截。
- 原本数据可读可写,但操作系统将对应页表中的访问权限设置为只读。当操作系统识别到该信息时,操作系统并不会将行为视为错误,而是作为重新申请内存拷贝内容的一种策略机制。此时操作系统会重新申请开辟空间,拷贝和修改数据,并修改页表中的映射关系。
3.7.fork创建子进程失败原因
在Linux中,fork()创建子进程失败通常由有以下两种原因:
- 系统中有太多的进程。操作系统内存不足。
- 实际上,用户使用fork()创建子进程是有次数限制的。当超过该范围时,fork创建子进程失败!
3.8.bash如何创建子进程?
我们看看程序最开始的那个printf,PPID是45106,那么45106是谁呢?
很明显了,就是我们自己的PID
这个bash创建子进程的过程肯定也调用了fork()
我们接着从这个故事说起:
“王婆为了保证自己在给别人说媒时,如果失败了,不会影响到自己,她是通过创建子进程来实现的。现在我的问题是,bash是如何创建子进程的?虽然我们还没有看或写bash的源代码,但我可以推断一下这个过程。
bash一定会调用fork来创建子进程,并让自己的代码继续执行命令行解释,而让子进程去执行解释新的命令。那么,bash自己就会继续接受用户的输入,并打印出提示符,等待用户输入新的命令。这就是为什么我们运行的所有命令都是在bash内部,因为所有的进程都是通过fork创建出来的。
当然,这只是问题的一半,后面还有更多内容需要探讨。但我们已经理解了创建子进程的过程,核心结论就是bash也是一个进程,当执行命令时,bash内部一定会为我们fork创建新的子进程,然后执行我们的代码。”
相关文章:
【Linux】进程3——PID/PPID,父进程,子进程
在讲父子进程之前,我们接着上面那篇继续讲 1.查看进程 mycode.c makefile 我们在zs_108直接编译mycode.c,直接运行,然后我们转换另一个账号来查看这个进程 我们可以通过ps指令来查看进程 我们就会好奇了,第二行是什么ÿ…...
开发常用的组件库
框架: Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org) React 官方中文文档 (docschina.org) Svelte 中文文档 | Svelte 中文网 SolidJS 反应式 JavaScript 库 页面样式: 网页端: 指南 |元素 (eleme.cn) Mint UI (mint-ui.github.io…...
深度解析地铁票务系统的技术架构与创新应用
在城市交通体系中,地铁作为一种快速、便捷的公共交通方式,已经成为现代都市生活的重要组成部分。而地铁票务系统的技术架构,则是支撑地铁运营的核心之一。本文将深度解析地铁票务系统的技术架构与创新应用,从系统设计、数据管理、…...
Python集合的基本概念和使用方法
目录 集合(Set) 基本概念 基本特性 基本操作 集合运算 成员测试 高级操作 集合推导式 总结 集合(Set) Python集合(Set)是Python语言中一个非常实用且强大的数据结构,它用于存储多个不…...
谷歌浏览器124版本Webdriver驱动下载
查看谷歌浏览器版本 在浏览器的地址栏输入: chrome://version/回车后即可查看到对应版本(不要点击帮助-关于Google chrome,因为点击后会自动更新谷歌版本) 114之前版本:下载链接 123以后版本:下载链接࿰…...
十大排序
本文将以「 通俗易懂」的方式来描述排序的基本实现。 🧑💻阅读本文前,需要一点点编程基础和一点点数据结构知识 本文的所有代码以cpp实现 文章目录 排序的定义 插入排序 ⭐ 🧐算法描述 💖具体实现 …...
微信小程序学习笔记(1)
文章目录 一、文件作用app.json:project.config.json:sitemap.json页面中.json 二、项目首页三、语法**WXML**和**HTML**WXSS 和CSS的区别小程序中.js文件的分类 一、文件作用 app.json: 当前小程序的全局配置,包括所有页面路径、窗口外观、…...
OpenGauss数据库-6.表空间管理
第1关:创建表空间 gsql -d postgres -U gaussdb -W passwd123123 CREATE TABLESPACE fastspace OWNER omm relative location tablespace/tablespace_1; 第2关:修改表空间 gsql -d postgres -U gaussdb -W passwd123123 ALTER TABLESPACE fastspace R…...
相约乌镇 续写网络空间命运与共的新篇章(二)
从乌镇峰会升级为世界互联网大会,既是展示互联网发展成果的技术盛会,也是尖端科技综合运用的宏大场景。从枕水江南散发出的“互联网之光”,到前沿技术的创新突破和场景应用,澎湃的是数字经济浪潮,激荡的是科技创新能量…...
【全网最简单的解决办法】vscode中点击运行出现仅当从 VS 开发人员命令提示符处运行 VS Code 时,cl.exe 生成和调试才可用
首先确保你是否下载好了gcc编译器!!! 检测方法: winR 打开cmd命令窗 输入where gcc(如果出现路径则说明gcc配置好啦!) where gcc 然后打开我们的vscode 把这个文件删除掉 再次点击运行代码,第一个出现…...
NFS共享存储服务
NFS共享存储服务 NFS:network file system ,在计算机网络中共享文件系统的协议。 计算机之间可以通过网络共享目录和文件,分为两个部分: 1、rpcbind:远程共享调用 2、nfs:共享服务,端口号:2…...
Docker面试整理-Docker 常用命令
Docker 提供了一套丰富的命令行工具,使得用户能够管理容器、镜像、网络和卷等资源。这里列出了一些常用的 Docker 命令: 镜像相关命令:docker pull [OPTIONS] NAME[:TAG|@DIGEST]: 从仓库拉取一个镜像或仓库。docker push NAME[:TAG]: 推送一个镜像或仓库到远程仓库。docker …...
Cinema 4D 2024 软件安装教程、附安装包下载
Cinema 4D 2024 Cinema 4D(C4D)是一款由Maxon开发的三维建模、动画和渲染软件,广泛用于电影制作、广告、游戏开发、视觉效果等领域。Cinema 4D允许用户创建复杂的三维模型,包括角色、场景、物体等。它提供了多种建模工具&#x…...
2024全国高考作文题解读(Chat GPT 4.0版本)
新课标I卷 阅读下面的材料,根据要求写作。(60分) 随着互联网的普及、人工智能的应用,越来越多的问题能很快得到答案。那么,我们的问题是否会越来越少? 以上材料引发了你怎样的联想和思考?请写…...
欧美北美南美国外媒体投稿和东南亚中东亚洲媒体海外新闻发稿软文推广营销策略有哪些?
在当今全球化的浪潮中,中国品牌正积极拓展海外市场,寻求更广阔的发展空间。面对国际竞争,有效的海外媒体发稿营销策略对于品牌国际化至关重要。以下是一些关键点和建议,以帮助品牌在海外市场取得成功。 深入了解目标市场…...
Rust-10-数据类型
Rust 标准库中包含一系列被称为 集合(collections)的非常有用的数据结构。大部分其他数据类型都代表一个特定的值,不过集合可以包含多个值。不同于内建的数组和元组类型,这些集合指向的数据是储存在堆上的,这意味着数据…...
C#面:PDB是什么东西? 在调试中它应该放在哪里
C# PDB(Program Database)是一种用于存储调试信息的文件格式。它包含了源代码文件、符号表和其他调试相关的信息,可以帮助开发人员在调试过程中定位和解决问题 在调试中,PDB文件应该与编译生成的可执行文件(如DLL或EX…...
C#--使用CMake构建C++程序调用示例
1.C代码 // example.cpp#include <iostream>extern "C" {__declspec(dllexport) void PrintMessage() {std::cout << "Hello from C!" << std::endl;} }2.CMakeLists.txt文件,用于使用CMake构建C库: # CMakeLis…...
三十七篇:大数据架构革命:Lambda与Kappa的深度剖析
大数据架构革命:Lambda与Kappa的深度剖析 1. 引言 在这个数据驱动的时代,我们面临着前所未有的挑战和机遇。随着数据量的爆炸性增长,传统的数据处理方法已无法满足现代业务的需求。大数据处理不仅涉及数据量的增加,还包括数据类型的多样化、数据来源的广泛性以及对实时数据…...
Vue3【十五】标签的Ref属性
Vue3【十五】标签的Ref属性 标签的ref属性 用于注册模板引用 用在dom标签上,获取的是dom节点 用在组件上,获取的是组件实例对象 案例截图 目录结构 代码 app.vue <template><div class"app"><h1 ref"title2">你…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...


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


