【Linux的进程篇章 - 进程终止和进程等待的理解】
Linux学习笔记---008
- Linux之fork函数、进程终止和等待的理解
- 1、fork函数
- 1.1、什么是fork?
- 1.2、fork的功能介绍
- 1.3、fork函数返回值的理解
- 1.4、fork函数的总结
- 2、进程的终止
- 2.1、终止是在做什么?
- 2.2、进程终止的3种情况
- 3、进程的终止
- 3.1、进程终止的三种情况
- 3.2、判断是否成功的顺序
- 3.3、如何终止
- 4、进程等待
- 4.1、直接先看结论
- 4.2、进程等待怎么处理呢?
Linux之fork函数、进程终止和等待的理解
前言:
前篇开始进行了解学习Linux的进程优先级、环境变量、地址空间等知识,这篇学习LInux的fork函数、进程终止和等待的理解等相关内容,深入地了解这个强大的开源操作系统。
/知识点汇总/
1、fork函数
前言:
进程:内核的相关管理数据结构(task_struct + mm_struct(内存分配表) + 页表) + 代码和数据
1.1、什么是fork?
直接man fork指令先在手册页查看以下基本信息:
补充:
man fork提示no manual entry for fork,则说明你当前没有安装man手册.
解决方法就是:
root权限下:yum -y install man-pages
基本概念:
fork() 是 Unix/Linux系统中的一个非常重要的系统调用,用于创建一个新的进程,这个新的进程是当前进程的复制品,称为子进程。这个复制过程包括父进程的代码、数据、堆、栈等内容都会被复制到子进程中。
1.2、fork的功能介绍
头文件:
#include <unistd.h>
原型:
pid_t fork(void)
返回值:
-1 ---- fork创建/调用子进程失败
==0 — 返回0是判断创建子进程成功
!=0 — 返回非负整数,表示返回新创建的子进程的进程ID,即,父进程返回的子进程pid
测试代码:
fork语句之后的程序,父子进程共享代码 —> fork创建一个进程,本质是系统中多一个进程;
而多一个进程,也就是多一个进程控制块(task_struct)
父进程的代码和数据是从磁盘加载来的;子进程的代码数据是哪里来的呢?
子进程会默认继承父进程的代码和数据。
我们为什么要创建一个子进程呢?
通常来说,是想要子进程执行和父进程不一样的代码,常规的程序都是单进程,不可能同时跑不同的代码内容。多进程情况下,则是合理的。
对于普通的单进程程序,只能有一个返回值;
对于多进程,那么为什么同一个程序同一个id,会有两个值,即是0又是!=0?
其次,fork怎么会有两个返回值,即返回两次呢?怎么做到的呢?
(涉及虚拟地址空间和父子写时拷贝知识,见这篇文章)
1.3、fork函数返回值的理解
fork也是一个函数,只不过属于系统调用。
然后平常的普通程序中的return在fork执行语句之后,即当执行完fork后,父子代码数据共享,
子进程代码中自然也有了return语句,所以父子进程各自被调度一次,那么自然就返回两次且值可以不同
引出一个概念:理论上,子进程被创建后是空的,自然其代码和数据就从父进程哪里拷贝获取,
就比如有window的任务管理器,有两个子进程由一个父进程创建,当此时关系结束一个子进程会影响其它进程?
当然不会的,逻辑上是父子等关系,实际上是分裂独立的。
所以进程一定要具备的特点就是:
具备独立性,即一个进程出现问题,绝不能影响其它进程。
进程 = 内核task_struct结构体(唯一的pid) + 代码程序(只读/共享的)和数据(父子数据各自独立,原则上数据需要分开)每一个进程都有一个自己唯一的标识符,叫做进程pid
fork()函数返回值:
1.子进程返回0
2.父进程返回子进程pid
为什么父进程返回的是子进程的pid,而子进程返回的是0?
因为父进程返回子进程的pid是对它更好的进程管理,而子进程返回值是判断创建子进程是否创建成功。
即:为了父进程对子进程进行标识,方便管理。
fork的常规用法:
1.一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
2.一个进程要执行一个不同的程序。例如子进程从fork()返回后,调用exec函数
fork()调用失败的原因:
1.系统中已有太多的进程。
2.实际用户的进程数超过了限制。
1.4、fork函数的总结
总结:
1.fork()函数在Linux中用于创建一个新的进程,这个新进程是当前进程的复制品,称为子进程。
2.fork()函数的特点在于它只被调用一次,但却能返回两次:一次在父进程中,一次在子进程中。
关于fork()函数的返回值,具体总结如下:
1.在父进程中,fork()函数返回新创建的子进程的进程ID(PID)。
2.这个PID是一个非负整数,用于唯一标识子进程。
3.在子进程中,fork()函数返回0。这是因为在子进程的上下文中,它自己是新创建的进程,所以没有其他的进程ID可以返回。返回0表示子进程成功创建。
4.如果fork()函数执行过程中发生错误,例如由于系统资源不足而无法创建新的进程,那么它会返回一个负值。这个负值通常表示特定的错误码,可以通过检查这个值来确定具体的错误原因。
5.需要注意的是,fork()函数创建的子进程是父进程的副本,包括父进程的代码、数据、堆、栈等内容都会被复制到子进程中。但是,这两个进程有不同的进程ID,并且它们从fork()函数的返回点开始独立执行。
6.因此,通过检查fork()函数的返回值,可以在父进程和子进程中执行不同的代码路径,从而实现并发执行或创建多进程程序。
2、进程的终止
2.1、终止是在做什么?
释放曾经的代码和数据所占据的空间;理解为释放内核的数据结构 ,否则–>僵尸态
2.2、进程终止的3种情况
测试代码:
echo $?
内建命令,打印的都是bash本身内部的变量数据。通常0:成功 非0:标识失败
其中?属于bash维护的一个变量
表示的是,父进程bash获取到的,最近的一个子进程退出的退出码
目的:
告诉父进程(关心结果),我把任务完成得怎么样
0:标识成功
非0:标识失败
非0的数值。一方面可表示失败,另一方面可表示失败的原因。即都有相应的错误描述。
测试代码:
父进程为什么要得到子进程的退出码呢?
答:要知道子进程的退出情况(成功、失败以及失败的原因),本质退出的情况信息,告诉给用户的。
退出码可以使用默认的,同时也可以自定义。
测试代码:
3、进程的终止
3.1、进程终止的三种情况
a、代码跑完,进程正确;
b、代码跑完,进程不正确。
可以通过进程的退出码决定。反馈原因:系统的 && 自定义退出码
c、代码异常终止,提前退出
操作系统发现了你的进程做了不该做的事情,就会被OS自主杀掉该进程。而且一旦出现异常,退出码就没有意义了。
需要关心的是为什么会出异常呢?
1.进程出异常,本质上是由操作系统(OS)向进程发生了一个信号。
2.我们可以查看进程退出的时候,退出的信号是多少,就可以判断,我的进程为什么异常了。
常见情况有:
a、段错误
b、栈溢出
3.2、判断是否成功的顺序
1.先确认是否是异常。
2.不是异常,就一定是代码跑完了。看退出码是否符合预期就行。
结论:
衡量一个进程退出,我们只需要两个数字(反馈给父进程):退出码和退出信号(2^2)。
task_struct()会包含一些字段信息。用于匹配反馈。
比如:exit_code和exit_signal
0 0
0 !0
!0 0
!0 !0
由退出码+退出信号 --》退出进程退出的原因。
3.3、如何终止
a、main()函数return 表示进程终止(非main函数,return,函数结束)
b、代码调用exit()函数,可在代码的任意位置退出,并冲刷缓冲区。
void exit(int status);
c、_exit()–系统调用,与exit的区别不会冲刷缓冲区。
void _exit(int status);
注意:这里所说的缓冲区,不是内核的缓冲区。
4、进程等待
4.1、直接先看结论
任何子进程,在退出的情况下,一般必须都要有父进程进行等待,进程在退出的时候,如果父进程不管不顾(不等待),直到退出进程,导致状态Z(僵尸状态),造成内存泄漏的问题。
为什么?
1.通过父进程等待,解决子进程退出的僵尸问题,回收系统资源(一定要注意)
2.获取子进程的退出信息,知道子进程是因为什么原因退出的(前提是如果需要的话,不是必须的)
4.2、进程等待怎么处理呢?
答:通过两个函数wait()函数和waitpid函数
阻塞等待测试代码:
查看指令:
while :; do ps ajx | head -1 && ps ajx | grep wait_process | grep -v
grep;sleep 1;done
说明:
头文件:
#include <sys/types.h>
#include <sys/wait.h>
函数原型:
pid_t wait(int* status);
返回值:
pid_t:等待成功时,子进程的pid给返回
参数:
status:等待父进程中,任意一个子进程的退出
功能:
用于解决子进程僵尸进程的。(defunct僵尸态)
如何理解阻塞等待子进程呢?
如果子进程没有退出,父进程就会一直处于进行阻塞等待的状态(死等)
子进程本身就是软件,父进程本身就是等待某种软件的条件就绪。
头文件:
#include <sys/types.h>
#include <sys/wait.h>
函数原型:
pid_t waitpid(pid_t pid,int* status,int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程id;
如果设置了选项,而调用waitpid发现没有已退出的子进程收集,则返回0;
如果调用中出错,则返回error会被设置成相应的值以指示错误所在。
1.正常返回时,返回的收集到的子进程id;
2.如果设置了option选项参数为WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
3.如果调用失败,则返回-1,且errno会被设置为相应的值得以指示错误所在。
参数:
pid=-1:等待任意个子进程的pid,与wait相同;
pid>0:等待其进程id与pid相等的子进程;
参数二说明:
status具有特定的格式。(低16位)
1.输出型参数,表示的是输出信息
高八位 – 退出状态
低八位 – 终止信号
2.参数二:status
WIFEXITED(status):若为正常终止子进程的状态,则返回真 — 作用是查看进程是否正常退出
WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码 ---- 查看进程的退出码
WNOHANG:若pid指定的子进程没有进程,则waitpid()函数返回0,不予以等待,若正常结束,则返回该子进程的ID。
非阻塞等待测试代码:
如果子进程没有退出,而父进程在进行执行waitpid进行等待,阻塞等待(scanf()…的原理)。
所以阻塞就是进程阻塞。
直到等待某种条件的发生(比如子进程的退出)
那么一些非阻塞等待,属于服务器宕机。
阻塞等待和非阻塞的等待本质区别就是判断是否有返回值,是否达成某成条件。
即:pid_t > 0:等待是成功的,子进程退出了,并且父进程回收成功;
pid_t < 0:等待失败了;
pid_t == 0:检测是成功的,只不过子进程还没有退出,需要你下一次进行重复等待。
相关文章:

【Linux的进程篇章 - 进程终止和进程等待的理解】
Linux学习笔记---008 Linux之fork函数、进程终止和等待的理解1、fork函数1.1、什么是fork?1.2、fork的功能介绍1.3、fork函数返回值的理解1.4、fork函数的总结 2、进程的终止2.1、终止是在做什么?2.2、进程终止的3种情况 3、进程的终止3.1、进程终止的三种情况3.2、…...
《策略模式(极简c++)》
本文章属于专栏- 概述 - 《设计模式(极简c版)》-CSDN博客 本章简要说明适配器模式。本文分为模式说明、本质思想、实践建议、代码示例四个部分。 模式说明 方案:策略模式是一种行为设计模式,它定义了一系列算法,将每…...
Python向文件里写入数据
直接上代码 name "测试" data name.encode("utf-8")# w特点:文件不存在则创建文件并在打开前清空 f open("db.txt", mode"wb")f.write(data)f.close()可以在 db.txt 文件里看到一句话 测试name "Testing" …...

【网站项目】校园订餐小程序
🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板ÿ…...
vue-指令v-for
<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>vue-指令v-for</title> </head> …...

Python项目1 外星人入侵_外星人
在本章中,我们将在游戏《外星人入侵》中添加外星人。首先,我们在屏幕上边缘附近添加一个外星人,然后生成一群外星人。我们让这群外星人向两边和下面移 动,并删除被子弹击中的外星人。最后,我们将显示玩家拥有的飞船数量…...

导入项目运行后,报错java: Cannot find JDK ‘XX‘ for module ‘XX‘
解决方案: 1、删除.idea和.iml文件 2、右击此module,点击 Open Module Settings 在 Module SDK 中选择所安装的java版本后,点击右下角 Apply,会再生成.idea文件; 那.iml文件呢?操作步骤: 按两下…...
JS rgb,hex颜色值转换
颜色值转化 rgb颜色值转换为hex颜色值(rgb>hex) hex颜色值转换为rgb颜色值(hex>rgb) 代码: const hex2Rgb (hex) > {return rgb(${parseInt(hex.slice(1, 3), 16)},${parseInt(hex.slice(3, 5), 16)},${p…...

Linux| Awk 中“next”命令奇用
简介 本文[1]介绍了在Linux中使用Awk的next命令来跳过剩余的模式和表达式,读取下一行输入的方法。 next命令 在 Awk 系列教程中,本文要讲解如何使用 next 命令。这个命令能让 Awk 跳过所有你已经设置的其他模式和表达式,直接读取下一行数据。…...

基于Springboot的箱包存储系统(有报告)。Javaee项目,springboot项目。
演示视频: 基于Springboot的箱包存储系统(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构&…...

JavaScript_语法--变量
1.4 变量 变量:一小块存储数据的内存空间 Java语言是强类型语言,而JavaScript是弱类型的语言 强类型: 在开辟变量存储空间时,定义了空间将来存储的数据的数据类型。只能存储固定类型的数据 弱类型: 在开辟变量存储空间…...
P1843 奶牛晒衣服
题目背景 熊大妈决定给每个牛宝宝都穿上可爱的婴儿装 。但是由于衣服很湿,为牛宝宝晒衣服就成了很不爽的事情。于是,熊大妈请你(奶牛)帮助她完成这个重任。 题目描述 一件衣服在自然条件下用一秒的时间可以晒干 a 点湿度。抠门…...

功能强大:JMeter 常用插件全解析
JMeter 作为一个开源的接口性能测试工具,其本身的小巧和灵活性给了测试人员很大的帮助,但其本身作为一个开源工具,相比于一些商业工具(比如 LoadRunner),在功能的全面性上就稍显不足。这篇博客,…...

vulhub之fastjson篇-1.2.27-rce
一、启动环境 虚拟机:kali靶机:192.168.125.130/172.19.0.1(docker地址:172.19.0.2) 虚拟机:kali攻击机:192.168.125.130/172.19.0.1 本地MAC:172.XX.XX.XX 启动 fastjson 反序列化导致任意命令执行漏洞 环境 1.进入 vulhub 的 Fastjson 1.2.47 路径 cd /../../vulhub/fa…...

基于springboot实现教师工作量管理系统项目【项目源码+论文说明】计算机毕业设计
基于springboot实现教师工作量管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了教师工作量管理系统的开发全过程。通过分析教师工作量管理系统管理的不足,创建了一个计算机管理教师工作…...

[StartingPoint][Tier1]Crocodile
Task 1 What Nmap scanning switch employs the use of default scripts during a scan? (哪些 Nmap 扫描开关在扫描期间使用默认脚本?) -sC Task 2 What service version is found to be running on port 21? 发现端口 21 上运行的服务版本是什么?…...

【Qt】:常用控件(四:显示类控件)
常用控件 一.Lable二.LCD Number 一.Lable QLabel 可以⽤来显⽰⽂本和图⽚. 代码⽰例:显⽰不同格式的⽂本 代码⽰例:显⽰图⽚ 此时,如果拖动窗⼝⼤⼩,可以看到图⽚并不会随着窗⼝⼤⼩的改变⽽同步变化 为了解决这个问题,可以在Widget中重写resizeEvent函数。当用户把窗口从A拖…...

gradio简单搭建——关键词简单筛选【2024-4-11优化】
gradio简单搭建——关键词简单筛选[2024-4-11 优化] 新的思路:标签自动标注界面搭建优化数据处理与生成过程交互界面展示 新的思路:标签自动标注 针对通过关键词,在文本数据中体现出主体的工作类型这一任务,这里使用展示工具grad…...

docker完美安装分布式任务调度平台XXL-JOB
分布式任务调度平台XXL-JOB 1、官方文档 自己看 https://www.xuxueli.com/xxl-job/#1.1%20%E6%A6%82%E8%BF%B0 2、使用docker部署 本人使用的腾讯云,安装docker暴露一下端口,就很舒服的安装这个服务了。 docker pull xuxueli/xxl-job-admin:2.4.03…...
java使用while循环输出2-100的所有素数
使用while循环输出2-100的所有素数,每行输出5个 分析:素数:只能被1和自己整除的自然数 public static void main(String[] args) {int num 2;int count 0;int count1 0;while (num < 100) {for (int i 1; i < num; i) {if (num % i 0) {count;}}if (count 2) {Sys…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...