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

[Linux] 进程创建、退出和等待

标题:[Linux] 进程创建、退出和等待

个人主页@水墨不写bug

(图片来源于AI)

目录

一、进程创建fork()

1) fork的返回值:

2)写时拷贝

​编辑3)fork常规用法

4)fork调用失败的原因

二、什么是进程退出

1)exit()函数于系统调用接口_exit()有什么区别?

三、僵尸进程和进程等待

1)父进程处理僵尸进程就需要进程等待

2)阻塞等待 

3)非阻塞等待:

 4)输出型参数status:

 总结:


正文开始:

一、进程创建fork()

         fork()是Linux操作系统提供的系统调用,作用是从一个已经存在的进程中创建一个新进程。新进程为子进程,原进程为父进程。

//头文件
#include <unistd.h>//函数原型
pid_t fork(void);//返回值:子进程中返回0,父进程返回子进程id,出错返回-1

 

进程调用fork,当控制转移到内核中的fork代码后,内核做:

  •         分配新的内存块和内核数据结构给子进程
  •         将父进程部分数据结构内容拷贝至子进程
  •         添加子进程到系统进程列表当中
  •         fork返回,开始调度器调度

        具体创建过程的内存图示见《Linux:初识进程地址空间》 ,子进程创建后这里仅仅给出一张图:

         最重要的是:当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。如下面的例子:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>int main()
{printf("fork之前 我的pid:%d\n",getpid());pid_t id = fork();printf("fork之后 我的pid:%d, fork的返回值:%d\n",getpid(),id);return 0;
}

        运行之后,结果如图:

         分析:

        在fork之前,只有一个进程,pid=48247;

        fork之后,父进程(pid=48247)创建出子进程(pid=48248):从父子进程的pid临近和fork给子进程的返回值为0,给父进程的返回值为子进程的pid可以看出来。

         fork之后,父子进程都执行到fork函数这一行,于是,fork之前的代码不会被执行,之后的代码会被父子进程各自执行一次

1) fork的返回值:

        给子进程返回0;

        给父进程返回子进程的pid;

        如果出错了,则子进程没有被创建出来,那么给仅有的一个进程(父进程)返回-1;

2)写时拷贝

        写时拷贝(copy-on-write)是一种内存管理技术,用于优化内存使用和提高性能。它是一种延迟拷贝的策略,在创建副本之前,共享资源将不会被复制。

        写时拷贝的原理是,当一个对象需要被拷贝时,不会立即进行拷贝操作。而是让新对象与原对象共享同一份资源。当其中一个对象修改了该资源时,才会将资源复制到新的对象上。这样可以减少内存的使用,避免了不必要的复制操作。

3)fork常规用法

        一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。

        一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。(进程替换函数)

4)fork调用失败的原因

        系统中有太多的进程;

        实际用户的进程数超过了限制;


二、什么是进程退出

         我们在当初学习C语言的时候,经常会malloc空间,但是动态内存开辟是会有失败的时候,malloc的最保险的用法:

    int size = 10;int* arr = (int*)malloc(size*sizeof(int));if(arr == NULL){perror("malloc fail:");exit(-1);}

         如果malloc失败,通常是非常严重的错误,这个时候进程已经没有必要继续运行下去了,这时候选择打印错误信息,如果调用exit函数退出。

        上述的进程退出只是进程退出的一种场景,其实进程想要退出,无非只有三种场景

1)代码运行完毕;

2)结果正确代码运行完毕,结果不正确;

3)代码异常终止

         当一个进程退出,我们在Linux终端可以使用:

echo $?

         命令查看最近一次进程退出的退出码。

        如果最近的一个进程正常退出,返回值是0;

 一个进程退出有什么方式?其实无非就是:

        1)从main函数返回;

        2)调用: exit();

        3)调用系统调用接口:   _exit();

对于其他的退出方式,一般就是我们主动退出:ctrl+C;

或者遇到空指针,或者被断言断死,其实这些退出方式有一个统称:被系统的信号杀死。

1)exit()函数于系统调用接口_exit()有什么区别?

         这两个函数的最本质功能是一样的,都是让进程退出,但是exit() 函数是对系统调用_exit()函数的封装。

        _exit函数是系统调用,exit是C语言的库函数。

进程调用_exit()会直接退出,但是调用exit会在退出之前做一些善后的处理:

        1. 执行用户通过 atexit或on_exit定义的清理函数。

        2. 关闭所有打开的流,所有的缓存数据均被写入(刷新缓冲区)

        3. 调用_exit

三、僵尸进程和进程等待

        在Linux系统中,僵尸进程(Zombie Process)是指已经结束执行的进程,但是其父进程还没有对其进行完全的清理操作,导致进程的资源没有被释放。

        僵尸进程的产生是因为操作系统在进程结束时不会立即清理相关的资源,而是会将其转变为僵尸进程,等待父进程来处理。父进程需要通过调用wait()或waitpid()系统调用来获取子进程的退出状态,并对其进行清理,释放相应的资源。如果父进程没有处理僵尸进程,那么僵尸进程会一直存在于系统中,占用系统资源。

        僵尸进程通常不会对系统造成直接的危害,因为它们已经停止运行。然而,如果有大量的僵尸进程存在,会浪费系统的进程表资源,并可能导致进程表耗尽的问题。此外,僵尸进程也可能是父进程出现问题或错误的一个指示,比如父进程无法正确处理子进程的退出状态。

1)父进程处理僵尸进程就需要进程等待

        父进程需要调用wait()/waitpid()等方法等待子进程退出。

2)阻塞等待 

        接口wait():等待任意一个子进程退出,但是在等待的过程中父进程什么事都干不了,这样的等待称为“阻塞等待”。

//头文件
#include<sys/types.h>
#include<sys/wait.h>pid_t wait(int*status);
//返回值:
//成功返回被等待进程pid,失败返回-1。//参数:
//输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

如果想要让父进程在等待的过程中做一些事情,就需要另一个接口:waitpid()

//头文件
#include<sys/types.h>
#include<sys/wait.h>pid_ t waitpid(pid_t pid, int *status, int options);//返回值:
//当正常返回的时候waitpid返回收集到的子进程的进程pid;//如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;//如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;//参数:
//pid:
//    Pid = -1, 等待任一个子进程,与wait等效。
//    Pid > 0 . 等待其进程ID与pid相等的子进程。
//status:
//    WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
//    WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
//options:
//    WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID

3)非阻塞等待:

        waitpid()接口如果设置了MNOHANG,那么进程走到这个接口之后,会查看一次子进程是否已经退出,如果已经退出,我们就可根据此时的返回值设置循环的出口;

        如果没有退出,则进程会执行waitpid() 之后的代码逻辑,直到下一次循环再次到达waitpid()这个接口处,再次查看,继续循环。

非阻塞等待实例:


void oper()
{sleep(1);printf("等待不妨碍做其他事\n");
}int main()
{pid_t id = fork();if(id == 0){printf("我是子进程,5秒后退出\n");sleep(5);exit(0);}else{int status = 0;printf("我是父进程,等待子进程退出\n");while(1){pid_t rid = waitpid(id,&status,WNOHANG);if(rid > 0){printf("父进程等到子进程退出\n");break;}oper();}}return 0;
}

运行结果:

 4)输出型参数status:

        什么是输出型参数:输出型参数就是我们穿一个参数,他的值是由操作系统填充的,因此我们在传参的时候需要传地址

        如果传递NULL,表示不关心子进程的退出状态信息。

        如果传递了status的地址,操作系统会根据该参数,将子进程的退出信息反馈给父进程。

        status不能简单的当作整形来看待,需要当作位图来看待,具体细节如下图(status是32位整形,高16位并不携带信息,只有status低16比特位携带信息):

 对于低16位:

        如果正常退出(没有被信号杀掉):那么低8位为0,次低8位表示退出码,这里的退出码,就是函数return的返回值,也是exit()括号内传的值。退出信号的意义是由我们自己赋予的。

        如果异常退出(进程被信号杀掉):退出码虽然可以手动获得,但是已经没有意义了,因为已经发生重大错误,导致进程都没有执行完就退出了 。这时低7位表示进程的退出信号,也就是操作系统发送给进程的异常信号。第8位暂时不考虑。

        低7位正好是2的七次方:64个标识,正好对应kill命令给进程发送的终止信号的个数:

 

 总结:

        正常退出,次低8位 退出码;

        异常退出,低7位 退出信号;

其实,为了便于记忆,有两个专门对status处理的

        

WIFEXITED:

        如果子进程正常退出,返回真;

WEXITSTATUS:

        返回status的次低8位退出码。

实例:


int main()
{pid_t id = fork();if(id == 0){int* p = NULL;*p = 10;exit(0);}else{int status = 0;wait(&status);printf("%d\n",status);if(WIFEXITED(status)){printf("程序正常退出:%d\n",WEXITSTATUS(status));}else{printf("程序挂了:%d\n",(status&0x7f));}}return 0;
}

运行结果:


完~

未经作者同意禁止转载 

相关文章:

[Linux] 进程创建、退出和等待

标题&#xff1a;[Linux] 进程创建、退出和等待 个人主页水墨不写bug &#xff08;图片来源于AI&#xff09; 目录 一、进程创建fork&#xff08;&#xff09; 1&#xff09; fork的返回值&#xff1a; 2&#xff09;写时拷贝 ​编辑3&#xff09;fork常规用法 4&#xff…...

微软推出针对个人的 “AI伴侣” Copilot 会根据用户的行为模式、习惯自动进化

微软推出了为每个人提供的“AI伴侣”Copilot&#xff0c;它不仅能够理解用户的需求&#xff0c;还能根据用户的日常习惯和偏好进行适应和进化。帮助处理各种任务和复杂的日常生活场景。 它能够根据用户的生活背景提供帮助和建议&#xff0c;保护用户的隐私和数据安全。Copilot…...

【QT】QT入门

个人主页~ QT入门 一、简述QT1、什么是QT2、QT的优势3、应用场景 二、QT的基本使用1、新建项目&#xff08;1&#xff09;选择项目模版&#xff08;2&#xff09;选择项目路径&#xff08;3&#xff09;选择构建系统&#xff08;4&#xff09;填写类信息设置界面&#xff08;5&…...

Linux 6.11版本发布

Linux 6.11版本的发布是Linux社区的一个重要里程碑&#xff0c;它不仅在实时计算、性能优化方面取得了显著进展&#xff0c;还在安全性上迈出了关键一步。 一、实时计算与性能优化 1.io_uring子系统支持 Linux 6.11引入了io_uring子系统的增强功能&#xff0c;特别是支持了b…...

CSS 参考手册

CSS 参考手册 概述 CSS(层叠样式表)是一种用于描述HTML或XML文档样式的样式表语言。它用于控制网页的布局和外观,使网页设计更加美观和响应式。CSS可以定义文本颜色、字体、布局、响应式设计等,是网页设计和开发中不可或缺的一部分。 基础语法 CSS的基本语法由选择器和…...

数据采集工具sqoop介绍

文章目录 什么是sqoop?一、Sqoop的起源与发展二、Sqoop的主要功能三、Sqoop的工作原理四、Sqoop的使用场景五、Sqoop的优势六、Sqoop的安装与配置 sqoop命令行一、Sqoop简介与架构二、Sqoop特点三、Sqoop常用命令及参数四、使用示例五、注意事项 什么是sqoop? Sqoop是一款开…...

扫盲:写给UI设计师的SCADA系统知识点

一、SCADA是什么&#xff0c;及其组成。 SCADA&#xff08;Supervisory Control And Data Acquisition&#xff0c;监控与数据采集系统&#xff09;是一种用于实时监控、控制和数据采集的自动化系统。 SCADA的组成部分&#xff1a; - 人机界面&#xff08;HMI*&#xff1a;提…...

类的特殊成员函数——三之法则、五之法则、零之法则

系统中的动态资源、文件句柄&#xff08;socket描述符、文件描述符&#xff09;是有限的&#xff0c;在类中若涉及对此类资源的操作&#xff0c;但是未做到妥善的管理&#xff0c;常会造成资源泄露问题&#xff0c;严重的可能造成资源不可用。或引发未定义行为&#xff0c;进而…...

计算机毕业设计 智慧物业服务系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…...

Python软体中使用SpaCy进行命名实体识别

Python软体中使用SpaCy进行命名实体识别 命名实体识别(Named Entity Recognition,NER)是自然语言处理(NLP)中的一个重要任务,它涉及识别文本中的命名实体,例如人名、地名、组织名等。SpaCy是一种流行的NLP库,提供了高效的NER功能。在本文中,我们将介绍如何使用SpaCy进…...

华为云技术深度解析:以系统性创新加速智能化升级

华为云技术深度解析&#xff1a;以系统性创新加速智能化升级 在当今数字化转型的浪潮中&#xff0c;云计算作为关键的基础设施&#xff0c;正以前所未有的速度推动着各行各业的智能化升级。作为全球领先的云服务提供商&#xff0c;华为云凭借其深厚的技术积累和创新实力&#…...

推理攻击-Python案例

1、本文通过推理攻击的方式来估计训练集中每个类别的样本数量、某样本是否在训练集中。 2、一种简单的实现方法&#xff1a;用模型对训练数据标签进行拟合&#xff0c;拟合结果即推理为训练集中的情况。 3、了解这些案例可以帮助我们更好的保护数据隐私。 推理攻击&#xff08;…...

find_box_3d

参数 &#xff08;ObjectModel3DScene, SideLen1, SideLen2, SideLen3, MinScore, GenParam : GrippingPose, Score, ObjectModel3DBox, BoxInformation) 入参介绍 1&#xff0c;ObjectModel3DScene&#xff0c; 输入的3d模型&#xff0c;这个模型最好是由xyx三通道点…...

Visual Studio2017编译GDAL3.0.2源码过程

一、编译环境 操作系统&#xff1a;Windows 10企业版 编译工具&#xff1a;Visual Studio 2017旗舰版 源码版本&#xff1a;gdal3.0.2 二、生成解决方案 打开Visual Studio 2017的x64本机生成工具&#xff0c;切换到gdal3.0.2源码根目录&#xff1b;执行generate_vcxproj.b…...

计算机网络——email

pop3拉出来 超出ASCII码范围就不让传了 这样就可以传更大的文件...

【Linux】信号知识三把斧——信号的产生、保存和处理

目录​​​​​​​ 1、关于信号的前置知识 1.1.什么是信号&#xff1f; 1.2.为什么要学习信号&#xff1f; 1.3.如何学习信号&#xff1f; 1.4.一些常见的信号 1.5.信号的处理方式 1.6.为什么每一个进程都可以系统调用&#xff1f; 2.信号的产生 2.1.kill命令产生信号…...

【国庆要来了】基于Leaflet的旅游路线WebGIS可视化实践

前言 转眼2024年的国庆节马上就要来临了&#xff0c;估计很多小伙伴都计划好了旅游路线。金秋十月&#xff0c;不管是选择出门去看看风景&#xff0c;还是选择在家里看人。从自己生活惯了的城市去别人生活惯了的城市&#xff0c;去感受城市烟火、去感受人文风景&#xff0c;为2…...

Element-UI Plus 暗黑主题切换及自定义主题色

1. 暗黑主题切换 在main.js中引入下面文件 import element-plus/theme-chalk/dark/css-vars.css安装 vueuse/core pnpm add vueuse/coreApp.vue 添加下面代码 使用了 useDark() 的页面才会从 localStorage中读取当前主题状态&#xff0c;否则&#xff0c;刷新页面就会恢复默…...

人工智能与机器学习原理精解【31】

文章目录 卷积神经网络CNN定义数学原理与公式计算与定理架构例子例题 全连接层的前馈计算定义数学原理与公式计算过程示例 参考文献 卷积神经网络 CNN 即卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;&#xff0c;是一类包含卷积计算且具有深度结构的前…...

如何安全地大规模部署 GenAI 应用程序

大型语言模型和其他形式的生成式人工智能(GenAI) 的广泛使用带来了许多组织可能没有意识到的安全风险。幸运的是&#xff0c;网络和安全提供商正在寻找方法来应对这些前所未有的威胁。 随着人工智能越来越深入地融入日常业务流程&#xff0c;它面临着泄露专有信息、提供错误答…...

verilog实现FIR滤波系数生成(阶数,FIR滤波器类型及窗函数可调)

在以往采用 FPGA 实现的 FIR 滤波功能&#xff0c;滤波器系数是通过 matlab 计算生成&#xff0c;然后作为固定参数导入到 verilog 程序中&#xff0c;这尽管简单&#xff0c;但灵活性不足。在某些需求下&#xff08;例如捕获任意给定台站信号&#xff09;需要随时修改滤波器的…...

OSPF的不规则区域

1.远离骨干非骨干区域 2.不连续骨干 解决方案 tunnel ---点到点GRE 在合法与非ABR间建立隧道&#xff0c;然后将其宣告于OSPF协议中&#xff1b; 缺点&#xff1a;1、周期和触发信息对中间穿越区域造成资源占用&#xff08;当同一条路由来自不同区域&#xff0c;路由器会先…...

大数据新视界 --大数据大厂之 Ibis:独特架构赋能大数据分析高级抽象层

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...

总结TypeScript相关知识

目录 引入认识特点安装使用变量声明类型推导 JS 和 TS 共有类型number类型boolean类型string类型Array类型null和undefined类型object类型symbol类型对象类型函数类型 可选和只读type 和 interface索引签名类型断言非空类型断言类型缩小严格赋值检测现象TS 新增类型字面量类型a…...

pdf怎么编辑修改内容?详细介绍6款pdf编辑器功能

■ pdf怎么编辑修改内容&#xff1f; PDF&#xff08;Portable Document Format&#xff09;作为一种广泛使用的文件格式&#xff0c;具有特点包括兼容性强、易于传输、文件安全性高、跨平台性、可读性强、完整性、可搜索性、安全性、可压缩性。 PDF文件本身是不可以直接进行编…...

【Blender Python】4.获取场景对象的几种方式

概述 有时候我们需要获取场景中已经添加或存在的对象。本节就总结在Blender Python中获取场景中对象的一些方法。 通过名称获取 py.data的objects()方法返回一个对象集合&#xff0c;可以使用键名或者下标形式获取具体的对象。 在默认新建的场景中&#xff0c;存在三个对象…...

鸿蒙harmonyos next flutter通信之EventChannel获取ohos系统时间

建立通道 flutter代码&#xff1a; EventChannel eventChannel EventChannel("com.xmg.eventChannel"); ohos代码&#xff1a; //定义eventChannelprivate eventChannel: EventChannel | null null//定义eventSinkprivate eventSink: EventSink | null null//建…...

Python 代码编写规范

本规范旨在为 Python 项目的代码编写提供一致性指南。它遵循 Python 社区的 PEP 8 标准&#xff0c;并结合了通用的编程最佳实践。 1. 编码风格 1.1 缩进 使用 4 个空格 作为缩进&#xff0c;不要使用制表符&#xff08;Tab&#xff09;。 def example():if True:print(&quo…...

k8s中pod的管理

一、资源管理 1.概述 说到k8s中的pod&#xff0c;即荚的意思&#xff0c;就不得不先提到k8s中的资源管理&#xff0c;k8s中可以用以下命令查看我们的资源&#xff1a; kubectl api-resources 比如我们现在需要使用k8s开启一个东西&#xff0c;那么k8s通过apiserver去对比etc…...

JavaScript中引用数据类型的浅拷贝

在JavaScript中&#xff0c;数据类型被分为“基本数据类型”和“引用数据类型”两大类。基本数据类型包括数值型、字符型、逻辑型、未定义型(undefined)、空型(null)和ES6新增的Symbol类型&#xff0c;引用数据类型包括数组、对象和函数。 当我们在程序中执行变量赋值操作的时候…...