【Linux】进程的基本属性|父子进程关系

在这里插入图片描述
在这里插入图片描述

个人主页:🍝在肯德基吃麻辣烫
我的gitee:Linux仓库
个人专栏:Linux专栏
分享一句喜欢的话:热烈的火焰,冰封在最沉默的火山深处

文章目录

  • 前言
  • 进程属性
    • 1.进程PID和PPID
    • 2.fork函数创建子进程
      • 1)为什么fork函数要给父进程返回子进程的pid,给子进程返回0?
      • 2)fork函数究竟在干什么?
      • 3)一个函数是如何做到返回两次的?
        • 写时拷贝
  • 总结


前言

本篇文章继上文的进程概念后,现在对进程概念有了一定的理解。前面说过,操作系统管理进程实际上是管理描述进程的PCB对象,而PCB对象是一堆进程属性的集合,那么进程都有哪些属性?本篇文章会详细写出来。


进程属性

我们知道,进程 = 描述该进程的PCB结构体对象 + 对应的数据和代码,每一个进程都是由操作系统进行管理的,进程的PCB(process ctrl block)对象是该进程的所有属性集合,所以,一个进程的多种属性,一定是放在PCB结构体里面的。

下面介绍进程的基本属性。


1.进程PID和PPID

什么是PID?PID我们可能不知道,但是ID我们应该是知道的,ID就是身份识别码。所以PID就是进程身份识别码(process ID)

在学校里面,每一个学生都有自己的学号,这个学号是独一无二的,进程也是类似,每一个进程的PID是独一无二的。

我们可以通过下面的指令来查看进程的PID。

ps axj | head -1

ps axj 指令可以查看当前用户下的所有进程,通过管道后,head指令提取管道文件的第一行并输出到显示器中。
结果如下:

在这里插入图片描述
第二个就是PID。

这里有一个注意的点,既然PID是该进程的唯一身份标识符,则该进程的PID一定是放在task_struct结构体中的,因为PID也是进程的属性之一。PID的本质是一个int类型。


这里有一个问题:我如何获取自己进程的PID?
在这里插入图片描述
从上面的描述过程中可以画出该图,ps axj指令能获取用户正在运行的所有进程,这些进程的信息本质上是ps axj这条程序员写的指令去调用操作系统开放的一个接口调用到的。

因为操作系统不相信任何人,它不敢也不给任何人访问我的所有进程的PCB结构体和各种信息。

所以可以想到,要想获取一个进程的PID,要通过一个系统调用接口来获取,这个接口叫做getpid()

在这里插入图片描述

下面来通过代码让操作系统给我们分配一个小小的进程:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int main()
  5 {
  6     pid_t pid = getpid();
  7    
  8     while(1)
  9     {
 10                                                               
 11         printf("I am a process,my pid is %d\n",pid);
 12         sleep(1);
 13     }
 14 
 15     return 0;
 16 }

运行后,再查询该进程的pid
在这里插入图片描述
发现通过系统的接口函数返回的pid和我们运行程序时正在跑的进程的pid是一样的

getpid()这个系统调用接口的工作原理是,我自己的进程调用getpid()函数,获取到我的PID后将结果返回给上层的一个变量。

不过这又有一个小细节,PPID是什么?


PPID,比PID多了一个P,这个P是parent的意思,也就是父进程的PID。
父进程就是该进程的父亲进程,就相当于我这个进程是父进程分配下来的。

我们再重新执行程序会发现,我原来的进程的PID变了。这就像是我们上大学后,发现我这个学校并不如意,我决定回去复读,第二年我比去年多考了几十分,可天意难料,我又被去年的学校录取了。这个过程中,去年我读的这所学校分配给我一个学号,今年再来到这所学校,也有一个学号,这两个学号肯定是不一样的。

那么,我们这样通过写代码的方式创建一个进程,它的父进程到底是谁?
在这里插入图片描述
我们查询一下可以发现,每次执行程序,它的PID都不同,但是PPID都是一样的,找到PID2215的那一行可以发现,它的COMMAND就是对应的进程对象。

由此可知,每一个自己创建的进程的父进程都是bash进程!
不过,bash进程的PID也是会变化的,重新启动xhell脚本就得到不一样的PID了。

2.fork函数创建子进程

fork函数的作用是:创建一个子进程。
在这里插入图片描述
这里是fork函数的基本说明,然而,重要的是fork函数的返回值:

在这里插入图片描述

  • 如果创建成功:返回子进程的pid给父进程,返回0给子进程。
  • 如果创建失败,返回-1给父进程,子进程则什么都不返回。

下面给一段代码演示一下fork函数。

  1 #include<stdio.h>                                                                                                                    
  2 #include<unistd.h>
  3 
  4 int main()
  5 {
  6     printf("begin:我是一个进程,我的pid是%d,我的ppid是%d\n",getpid(),getppid());
  7     pid_t id = fork();
  8 
  9     if(id == 0)
 10     {
 11         //子进程
 12          while(1)
 13          {
 14             printf("我是子进程,我的pid是%d,我的ppid是%d\n",getpid(),getppid());
 15             sleep(1);
 16          }
 17     }
 18 
 19     else if(id > 0)
 20     {
 21         //父进程
 22         while(1)
 23         {
 24             printf("我是父进程,我的pid是%d,我的ppid是%d\n",getpid(),getppid());
 25             sleep(1);
 26         }
 27     }
 28 
 29     return 0;
 30 }

在这里插入图片描述

运行后你会发现结果如上:

执行第一个printf语句后,打印的pid和ppid就是当前正在运行进程的pid和ppid。

然后你会发现同时执行了if else if 两个语句块!

这到底是什么原因呢?

看运行结果可知,父进程的pid和ppid跟第一个printf打印出来的是一模一样的!说明父进程就是当前这个程序的进程!而子进程是父进程的一个分支

这里还有几个问题需要解决:


1)为什么fork函数要给父进程返回子进程的pid,给子进程返回0?

举个简单的例子,每一个孩子一定只有一个亲生父亲,但是每一个父亲可能会有多个孩子,这是毋庸置疑的。假如一个父亲有5个孩子,父亲说:孩子,你过来。然后5个孩子齐刷刷地跑过来,父亲到底叫的是哪个孩子呢?

所以就必须让父进程知道每一个子进程的标识符!
也就是要知道每一个子进程叫什么名字,返回子进程的pid给父进程是最合理的。

而对于子进程来说,它的父进程只有一个且不花什么代价就能找到父进程pid,所以只需要返回0给子进程作为标识即可。


2)fork函数究竟在干什么?

要知道,fork函数的功能是创建一个子进程,可是到底什么叫做创建一个子进程?
其实,创建子进程,无非就是系统中多了一个进程!

我们知道,进程 = PCB数据结构 + 自己的代码和数据,多一个进程就是在操作系统中多管理一个PCB数据结构和一段代码罢了。

可是,子进程刚创建出来并没有代码和数据,所以子进程只能去找父进程的代码和数据来执行。

在这里插入图片描述
这就是为什么,fork函数之后的所有代码是父子进程共享的!

这就解释了从fork函数之后下面的代码,父进程和子进程都能跑的原因。

那为什么要创建子进程?
因为在不同的场景中,我们需要让父子进程执行不同的代码块!
前面我们说过,fork函数之后父子进程共享代码,虽然是共享,实际上就是为了让父子进程执行不同的代码块,完成不同的工作从而协调起来。


3)一个函数是如何做到返回两次的?

前面说过,fork函数之后的代码父子共享。 但是,fork函数,也是一个函数,是在系统内部实现的,调用的时候会在fork函数内部创建进程,大致会做几件事情:

在这里插入图片描述

1.创建子进程的PCB对象
2.初始化子进程的PCB
3.让子进程指向父进程的数据和代码
4.让父子进程都能被CPU调度运行

前面说过,父子进程的代码是共享的,所以在return id这条代码,一定是父子共享的! 因为在return id语句执行之前,已经做好了创建子进程的工作。CPU可以单独调用父子进程执行不同的工作。

所以return id这条语句被执行了两次!

可是前面说过,子进程是没有任何数据和代码的,子进程的代码也是人家父进程的,何况只有一个pid变量,该怎么接收两个返回值呢?

这里引出一个进程的性质:

任何平台下,任何一个进程在运行时,都具有独立性!

如何理解独立性?

我们在windows系统下面,我现在打开网页版csdn和xhell还有qq,突然我的qq崩溃了,但这并不影响我的网页运行,也不影响我在听音乐,这就是独立性,各个进程运行互不干扰

既然进程有独立性,这就保证了每个进程之间不能有任何瓜葛,必须让它们割裂!
所以,父子进程一定不能访问同一份数据!

在这个前提下,子进程要想运行起来,必须要有自己的一份数据,所以,子进程只能想办法把父进程的数据拷贝下来

这样就能够保证父子进程既能够保持父子进程的代码共享,又能保证父进程的数据不能被修改。保证父子共享代码的同时,又保证了进程的独立性。

可是,如果我的父进程有很多很多个变量,而子进程拷贝了父进程的数据,又不会去改这些变量,甚至不访问这些变量,就会造成在内存中有两份冗余的数据!为了解决这个问题,程序员想出一个好办法:写时拷贝

写时拷贝

写时拷贝是指:子进程在执行了return id这一条语句后,不会立刻去拷贝父进程的所有数据,而是先看子进程需要什么数据,再根据这些数据开辟需要的空间,这样就能避免数据冗余的情况。

后续如果子进程还需要数据,操作系统再给子进程空间并拷贝过去即可。

在这里插入图片描述

通过写时拷贝,实现父子进程的独立性,保证父进程的数据不会被修改,又能保证父子进程的代码共享!

总结

1.本篇文章讲述了进程的最基本的属性:进程的PID和PPID,PID是每个进程独有的标识序号,PPID是该进程的父进程的标识序号。
2.通过fork函数创建出来子进程。什么是创建子进程,以及给了一个案例,运行后发现了令人震惊的结果,提出了fork函数之后父子进程的代码是共享的,但是每个进程都具有独立性,父进程的数据绝对不能让子进程修改,从而产生写时拷贝的做法,来保证父子进程既能够具有独立性,也能让父子进程代码共享。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【深入了解PyTorch】PyTorch模型解释性和可解释性:探索决策过程与预测结果的奥秘

【深入了解PyTorch】PyTorch模型解释性和可解释性:探索决策过程与预测结果的奥秘 PyTorch模型解释性和可解释性:探索决策过程与预测结果的奥秘1. 引言2. 梯度可视化3. 特征重要性分析4. 结论PyTorch模型解释性和可解释性:探索决策过程与预测结果的奥秘 在机器学习和深度学习…

【Docker】Docker使用之容器技术发展史

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集 &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0…

嵌入式学习之字符串

通过今天的学习&#xff0c;我主要提高了对sizeof 和 strlen、puts()、gets()、strcmp 、strncmp、strstr、strtok的理解。重点对sizeof的使用有了更加深刻的理解

山东布谷科技直播软件开发WebRTC技术:建立实时通信优质平台

在数字化的时代&#xff0c;实时通信成为了人们远程交流的主要方式&#xff0c;目前市场上也出现了很多带有实时通信交流的软件&#xff0c;实时通信符合人们现在的需求&#xff0c;所以在直播软件开发过程中&#xff0c;开发者也运用了实时通信技术为直播软件加入了实时通信的…

整理分享Springboot项目中java实现将数据库表中指定表中的的数据按条件导出生成Excel表格的功能实现(学习笔记)

在Spring Boot中&#xff0c;我们可以使用Apache POI库来实现将数据库表中的数据导出为Excel表格。可以根据条件从数据库中查询数据并将其导出为Excel&#xff1a;如下 准备工作&#xff1a;首先&#xff0c;确保在你的项目中引入Apache POI依赖。在pom.xml文件中添加以下依赖项…

系统架构设计专业技能 · 网络规划与设计(三)【系统架构设计师】

系列文章目录 系统架构设计专业技能 网络规划与设计&#xff08;三&#xff09;【系统架构设计师】 系统架构设计专业技能 系统安全分析与设计&#xff08;四&#xff09;【系统架构设计师】 系统架构设计高级技能 软件架构设计&#xff08;一&#xff09;【系统架构设计师…

MAVEN利器:一文带你了解MAVEN以及如何配置

前言&#xff1a; 强大的构建工具——Maven。作为Java生态系统中的重要组成部分&#xff0c;Maven为开发人员提供了一种简单而高效的方式来构建、管理和发布Java项目。无论是小型项目还是大型企业级应用&#xff0c;Maven都能帮助开发人员轻松处理依赖管理、编译、测试和部署等…

Centos8安装docker并配置Kali Linux图形化界面

鉴于目前网上没有完整的好用的docker安装kali桌面连接的教程&#xff0c;所以我想做一个。 准备工作 麻了&#xff0c;这服务器供应商提供的镜像是真的纯净&#xff0c;纯净到啥都没有。 问题一&#xff1a;Centos8源有问题 Error: Failed to download metadata for repo ap…

【实战】十一、看板页面及任务组页面开发(一) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十三)

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…

shell编程

1.特殊变量 $n &#xff1a;n为数字&#xff0c;$0代表该脚本名称&#xff0c;$1-$9代表第一到第九个参数&#xff0c;十以上的参数&#xff0c;十以上的参数需要用大括号包含&#xff0c;如${10} $# &#xff1a;获取所有输入参数个数 $#&#xff1a;命令行中所有的参数&…

卡巴斯基为基于Linux的嵌入式设备推出专用解决方案

导读卡巴斯基在其卡巴斯基嵌入式系统安全产品中引入了对 Linux 的支持。这种适应性强的多层解决方案现在为基于Linux的嵌入式系统、设备和场景提供优化的安全&#xff0c;合通常适用于这些系统的严格监管标准。 卡巴斯基在其卡巴斯基嵌入式系统安全产品中引入了对 Linux 的支持…

非谓语动词1(背)

非谓语动词的概述 for:对某人来说做某事是怎么怎么样的 of&#xff1a;人的内在品质,你真的太怎么怎么样了 非谓语动词作主语 非谓语动词作宾语 非谓语动词作表语 现在分词作表语时时常时形容事物的 过去分词作表语一般是形容人的 非谓语动词作补语 注&#xff1a;无论是使役…

星星之火:国产讯飞星火大模型的实际使用体验(与GPT对比)

#AIGC技术内容创作征文&#xff5c;全网寻找AI创作者&#xff0c;快来释放你的创作潜能吧&#xff01;# 文章目录 1 前言2 测试详情2.1 文案写作2.2 知识写作2.3 阅读理解2.4 语意测试&#xff08;重点关注&#xff09;2.5 常识性测试&#xff08;重点关注&#xff09;2.6 代码…

使用wxPython和PyMuPDF在Python中显示PDF目录的实现

展示如何使用wxPython和PyMuPDF库在Python中选择PDF文件并将目录显示在列表框中。 简介&#xff1a; 在本篇教程中&#xff0c;我们将学习如何使用wxPython和PyMuPDF库在Python中选择PDF文件&#xff0c;并将其目录显示在一个列表框中。这将使用户能够方便地浏览PDF文档的目录…

Flask Web开发实战(狼书)| 笔记第1、2章

前言 2023-8-11 以前对网站开发萌生了想法&#xff0c;又有些急于求成&#xff0c;在B站照着视频敲了一个基于flask的博客系统。但对于程序的代码难免有些囫囵吞枣&#xff0c;存在许多模糊或不太理解的地方&#xff0c;只会照葫芦画瓢。 而当自己想开发一个什么网站的时&…

使用Java服务器实现UDP消息的发送和接收(多线程)

目录 简介&#xff1a;1. 导入必要的库2. 创建服务器端代码3. 创建客户端代码4. 实现多线程处理5. 测试运行示例代码&#xff1a;函数说明服务器端代码说明&#xff1a;客户端代码说明&#xff1a; 总结&#xff1a; 简介&#xff1a; 在本篇博客中&#xff0c;我们将介绍如何…

C语言学习笔记---数据的存储详解

C语言程序设计笔记---015 C语言数据的存储1、数据类型的意义1.1、unsigned与signed数据类型例程11.2、补码与原码相互转换例程2 2、大小端的介绍2.1、大小端的例程12.2、大小端的例程2 --- 判断当前编译器环境属于大端或小端 3、综合练习题探究数据的存储3.1、练习题13.2、练习…

Martin_DHCP_V3.0 (DHCP自动化泛洪攻击GUI)

Github>https://github.com/MartinxMax/Martin_DHCP_V3.0 首页 Martin_DHCP_V3.0 自动化DHCP洪泛攻击 Martin_DHCP_V3.0 使用方法 安装三方库 #python3 1.RunMe_Install_Packet.py 攻击路由器 #python3 Martin_DHCP_Attack.py 填写网卡 填写攻击次数 开始运行

Tomcat的动静分离以及多实例部署

一、动静分离 Nginx实现负载均衡的原理&#xff1a; Nginx实现负载均衡是通过反向代理实现Nginx服务器作为前端&#xff0c;Tomcat服务器作为后端&#xff0c;web页面请求由Nginx服务来进行转发。 但不是把所有的web请求转发&#xff0c;而是将静态页面请求Ncinx服务器自己来处…

计算机视觉五大核心研究任务全解:分类识别、检测分割、人体分析、三维视觉、视频分析

目录 一、引言1.1 计算机视觉的定义1.1.1 核心技术1.1.2 应用场景 1.2 历史背景及发展1.2.1 1960s-1980s: 初期阶段1.2.2 1990s-2000s: 机器学习时代1.2.3 2010s-现在: 深度学习的革命 1.3 应用领域概览1.3.1 工业自动化1.3.2 医疗图像分析1.3.3 自动驾驶1.3.4 虚拟现实与增强现…