【Linux笔记】进程等待与程序替换

一、进程的终止

1、进程退出码

在讲解进程的终止之前,先要普及一下进程的退出码概念。

我们父进程之所以要创建子进程,就是为了让子进程运行不一样的任务,那么对于子进程执行的这个任务执行完毕后的结果是否正确或者是否出差错,我们父进程要对这个结果进行验收。

从而就引出了进程退出码和进程退出信号的概念,其中退出码就是main函数的返回值:

我们在命令行中可以使用“echo $?”这串指令来查看最近一个进程的退出码:

如果我们将main函数的返回值改成其他数,那echo $?查出来的值也会随之变化:

一般默认的退出码为0则表示程序运行正确,是其他数则表示程序出了问题,即运行失败。

所以0表示成功,非0就表示失败,具体非零的数字那个对应的是哪种错误,我们可以自己定义。

当然C语言内置的也有一套错误码对应的字符串解释,我们可以调用strerror这个函数来查看:

2、进程终止的方法

在main函数中直接return

在main函数中直接return的方法其实上面已经演示过了,这个其实很容易理解,main函数是程序的入口也是程序结束的地方,所以我们父进程需要回收的就是main函数的返回情况。

使用exit接口

还还有一种方式就是,使用exit接口,这是一个C语言提供的接口:

如果仅放在main函数中,其实它和return是同级的,但是它其实比return的级别还要高,因为它放在任何地方都可以让程序直接结束:

从其运行的结果我们可以看出exit的级别是要比return高的,因为程序的退出码为5,而且后面的打印语句也没有执行。

使用_exit系统调用

还有一个与exit类似的接口_exit也可以用来结束进程:

它是一个系统调用,它的正常使用其实是和exit接口执行的结果一样的,比如把上一段程序中的exit改成_exit后,我们会发现它们执行的结果一模一样:

exit与_exit的区别

那它两到底有什么区别呢?

它两的区别其实就在于它们前者是C库函数,后者是系统调用。

我们先来看一个现象:

我们会发现,程序执行后确实是成功了,因为退出码正确了,但是程序并没有输出任何结果。

不急我们再来对程序进行微微的改动,在printf里面加上\n:

加上\n之后就打印出来了。

然后我们可以再对比一下exit:

经过观察会发现,exit不论是否加上\n它都有结果打印出来,那这是怎么一回事呢?

其实上面的结果的本质就是exit在退出前会刷新缓冲区,而_exit在退出前不会刷新缓冲区。因为缓冲区的刷新条件之一就是遇到了\n,所以只要加上了\n,exit和_exit都会有结果输出,而从_exit退出之前没有刷新缓冲区的结果来看,我们可以推测出缓冲器肯定不在操作系统内部。

道理就是_exit是系统调用肯定是比库函数exit更接近操作系统内部的,但是偏偏exit刷新了,而_exit没有刷新,如果缓冲区是在操作系统内部,那exit都刷新了,为什么更接近操作系统的_exit没有刷新呢?

二、进程等待、

我们知道,如果子进程先于父进程退出,那么子进程就会变成僵尸进程,如果僵尸进程一直不被回收,就会造成内存泄漏问题。

所以,进程等待就是为了回收子进程从而解决子进程僵尸问题造成的内存泄漏而生的。

1、如何进程进程等待

wait的使用

解决进程等待,我们使用的是wait系统调用:

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程 pid ,失败返回 -1
参数:
输出型参数,获取子进程退出状态 , 不关心则可以设置成为 NULL
其中的它的参数我们可以先不管,直接传NULL即可。
运行结果:

解释wait的阻塞等待

而且wait的等待方式为阻塞等待,所谓阻塞等待即父进程在等待子进程的过程中会一直停滞着,不会执行任何任务,下面以一个例子来演试一下:

代码:

运行结果:

从结果我们可以看出,在子进程执行期间,父进程其实也已经在执行了,但是一直到子进程执行完毕之后父进程才执行结束。所以在子进程执行期间,父进程一直在做阻塞等待。

waitpid的使用

waitpid相比于wait来说选择性更多,wait只能等待任意一个进程,而waitpid既能等待任意一个进程也能等待指定进程,wait只能阻塞等待,而waitpid既能阻塞等待也能非阻塞等待。

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
    当正常返回的时候 waitpid 返回收集到的子进程的进程 ID
    如果设置了选项 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
比如在上面的代码中,我们将wait直接改成waitpid,然后将第一个参数改成-1或子进程的id值,结果都是一样的:
其中第三个参数设置为0表示的是阻塞等待,第二个参数先不管。

解释waitpid的第二个参数

上面说过进程等待其实是为了解决子进程僵尸状态所造成的内存泄漏问题,但是进程等待还有第二个目的,就是检查子进程的运行结果,即获取子进程的退出码和终止信号。而这个工作就是有waitpid的第二个参数来做的。

wait和waitpid都有这个status参数,它其实是一个输出型参数,即通过外部传参的方式,在内部对参数进程修改,然后外部得到对应的状态。

这个status虽然是一个32位的整型,但我们并不整体使用它,而是只是用它的低16位:

但是我们想直接通过打印出status的只来判断退出信息的话,是有点奇怪的,例如:

正如上面所说的,status并不是整体使用的,而是在进程正常退出时,退出码“直接被放到次低八位”的:

如果想要提取出10来,有两种方法,一种是直接进行位运算,一种是使用上面提供的接口:WEXITSTATUS。

位运算有点儿不好记,而且久了也容易忘,所以我这里就只演示使用函数的情况:

再来演示一下进程异常退出的情况,进程异常退出时,退出码其实是没有意义的,所以我们主要看的是,终止信号。

比如我们可以设置一个常见的空指针异常:

这里就只能使用位运算了,这里的位运算没什么,记住就行。

如果不想记的话,也可以用上面提到的WIFEXITED函数来直接判断子进程是否正常退出,只是不能看出进程异常对应的信号值:

waitpid等待多个进程

那么如何使用waitpid来等待多个子进程呢?

其实很简单,我们创建多个子进程使用的是循环的方式,那我们在等待的时候也使用循环等待即可,先演示阻塞等待的。

演示waitpid的轮训等待(非阻塞等待)

waitpid的第三个参数options默认支持两种等待方式,阻塞等待和非阻塞等待。设置为0表示阻塞等待,而设置成另一个数(宏)WNOHONG则表示,非阻塞等待。

而如果我们直接像使用wait一样使用waitpid的非阻塞等待的话,就会发现父进程直接就结束了,并没有继续等:

显然父进程是并没有成功回收子进程的。因为它先于子进程退出了。

如果我们想让如进程成功回收子进程,就必须使用循环的方式:

三、程序替换

1、程序替换与创建子进程的区别

我们之前是通过创建子进程的方式来让进程“分流”的执行不同的任务,但是子进程执行的任务本质上还是父进程代码的一部分。如果我们想要让子进程执行新的代码和访问新的数据,不再和父进程有瓜葛,就得使用进程替换的方式。

2、进程程序替换的接口

先介绍execl接口

execl这个接口可以为我们执行进程替换的工作,所谓的进程替换其实并不是创建新进程,而是将进程的代码和数据替换:

它的第一个参数表示你要替换的进程的路径+文件名,而后面的可变参数列表则表示你想要怎样执行这个进程,因为各种进程执行所对应的执行选项不同,所以这里需要用可变参数列表,需要注意的是在可变参数列表的最后,一定要传一个NULL,表示参数传递完毕。

先拿我们最常用的“ls”这个指令来演示:

这样我们就可以在我们自己写的C语言代码中调用我们系统的指令了。

然后我们再来看一个现象:

上面的结果中只打印了replace before而没有打印replace after,这其实就说明了进程替换其实是将代码替换掉的。代码都不同了,当然就没有打印啦。

然后我们再来看看它有没有创建新进程:

从结果可以看出,前面创建的子进程后后面付进程等待到的子进程id是一样的,所以进程替换并没有创建新进程。

execlp:

execlp与execl的区别在于,execl需要传文件路径+文件名,而execlp只需要传文件名就行了,多出来的这个p可以理解为自带路径path的含义:

execv:

这个接口,与之前两个不同之处在于,它后面的命令函参数是一个一个数字的形式传递的。他后面的这个v其实就是vector的意思:

剩下的几个接口其实在介绍完上面的三个接口之后,就可以类推出来他们的含义和用法了,如上所述,如果带‘l’则表示后面的命令函参数一列表的形式传递,如果带‘p’则表示它是自带路径的,传第一个参数的时候就只需要传文件名即可,如果是带'v'的,则表示他后面的命令行参数是以数组的形式传递。

以此类推即可。

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

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

相关文章

QT上位机开发(利用tcp/ip访问plc)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 plc是工控领域很重要的一个器件。简单的plc一般就是对io进行控制&#xff0c;但是复杂的plc&#xff0c;还可以控制电机、变频器&#xff0c;在工业…

在CentOS上设置和管理静态HTTP网站的版本控制

在CentOS上设置和管理静态HTTP网站的版本控制是一项重要的任务&#xff0c;它可以帮助您跟踪和回滚对网站所做的更改&#xff0c;确保数据的一致性和完整性。以下是在CentOS上设置和管理静态HTTP网站的版本控制的步骤&#xff1a; 安装版本控制系统在CentOS上安装Git或其他版本…

打铁需要自身硬,我敢和欧系谬论硬刚源自实力与信心

我揭露欧系数学荒谬的目的是驱逐纯粹数学出中国&#xff0c;以恢复中华数学体系、最终让中华数学领导世界&#xff1b;我从来不隐瞒自己的“野心”&#xff0c;我对此有着绝对的信心。民族情怀是中国数学人的短板 纯粹数学是欧洲人的文化、是欧系数学的主体&#xff0c;它的历…

CMake+大漠插件的应用开发——处理dm.dll,免注册调用大漠插件

文章目录 CMake大漠插件的应用开发——处理dm.dll&#xff0c;免注册调用大漠插件说明环境项目结构配置编译环境编码-直接调用 dll编码-生成tlh文件&#xff0c;便于提示 CMake大漠插件的应用开发——处理dm.dll&#xff0c;免注册调用大漠插件 说明 网上有一种使用方式是&am…

SSM整合(实现简单查询功能)

在名为ssm的数据库内创建表 CREATE TABLE account (id int(11) NOT NULL AUTO_INCREMENT,name varchar(20) DEFAULT NULL,money double DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8; 创建工程 pom.xml <?xml version"1.0" encoding&quo…

野牛物联网-阿里云配置流程

1、 概述&#xff1a; 本文围绕阿里云物联网平台&#xff0c;实现设备上云、设备上报消息、云端订阅设备消息、云端下发指令到设备等服务&#xff0c;以野牛物联网YNK-MN316设备接入物联网平台为例&#xff0c;介绍设备如何接入物联网平台&#xff0c;向平台上报消息等。帮助您…

MySQL中约束是什么?

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

MetaGPT前期准备与快速上手

大家好&#xff0c;MetaGPT 是基于大型语言模型&#xff08;LLMs&#xff09;的多智能体协作框架&#xff0c;GitHub star数量已经达到31.3k。 接下来我们聊一下快速上手 这里写目录标题 一、环境搭建1.python 环境2. MetaGpt 下载 二、MetaGPT配置1.调用 ChatGPT API 服务2.简…

写在学习webkit过程的前面

webkit起源于KHTML&#xff0c;是KDE开源项目的KHTML和KJS引擎的一部分。在它的诞生和发展过程中&#xff0c;由两家著名的公司参与开发过程中&#xff0c;造成两次裂变。诞生两个内核webkit和blink&#xff0c;并发展和产生了两个主流的浏览器&#xff0c;分别为safari和chrom…

全网快递查询工具:批量查询,提升工作效率的利器

在快递行业日新月异的今天&#xff0c;高效、准确的快递信息管理显得尤为重要。固乔快递查询助手正是一款专为快递网点设计的实用工具&#xff0c;它可以帮助您快速、批量查询全网快递单号&#xff0c;为您的网点运营带来诸多便利。 一、固乔快递查询助手的用途 批量查询&…

跨境商城系统如何开发代购商城、国际物流、一件代发等功能?

跨境商城系统的开发涉及到多个方面&#xff0c;其中代购商城、国际物流和一件代发等功能是其中的重要组成部分。本文将详细介绍如何开发这些功能&#xff0c;以帮助跨境商城系统更好地满足市场需求。 一、代购商城的开发 代购商城是跨境商城系统中的重要功能之一&#xff0c;它…

FilterQuery过滤查询

ES中的查询操作分为两种&#xff1a;查询和过滤。查询即是之前提到的query查询&#xff0c;它默认会计算每个返回文档的得分&#xff0c;然后根据得分排序。而过滤只会筛选出符合条件的文档&#xff0c;并不计算得分&#xff0c;并且可以缓冲记录。所以我们在大范围筛选数据时&…

阅读笔记lv.1

阅读笔记 sql中各种 count结论不同存储引擎计算方式区别count() 类型 责任链模式常见场景例子&#xff08;闯关游戏&#xff09; sql中各种 count 结论 innodb count(*) ≈ count(1) > count(主键id) > count(普通索引列) > count(未加索引列)myisam 有专门字段记录…

Python 全栈体系【四阶】(十二)

第四章 机器学习 十五、朴素贝叶斯 朴素贝叶斯是一组功能强大且易于训练的分类器&#xff0c;它使用贝叶斯定理来确定给定一组条件的结果的概率&#xff0c;“朴素”的含义是指所给定的条件都能独立存在和发生。朴素贝叶斯是多用途分类器&#xff0c;能在很多不同的情景下找到…

leetcode17 电话号码的字母组合

方法1 if-else方法 if-else方法的思路及其简单粗暴&#xff0c;如下图所示&#xff0c;以数字234为例&#xff0c;数字2所对应的字母是abc&#xff0c;数字3所对应的是def&#xff0c;数字4所对应的是ghi&#xff0c;最后所产生的结果就类似于我们中学所学过的树状图一样&…

跟着cherno手搓游戏引擎【4】窗口抽象、GLFW配置

引入GLFW&#xff1a; 在vendor里创建GLFW文件夹&#xff1a; 在github上下载&#xff0c;把包下载到GLFW包下。 GitHub - TheCherno/glfw: A multi-platform library for OpenGL, OpenGL ES, Vulkan, window and input修改SRC/premake5.lua的配置&#xff1a;12、13、15、36…

异地快速传输大文件的常用方法

在企业间的信息沟通与协作中&#xff0c;快速传输大文件是一项基本需求。然而&#xff0c;跨地域传输庞大文件时&#xff0c;往往面临着网络带宽、文件大小、传输速度以及数据安全等多方面的挑战。本文将介绍四种常用的异地快速传输大文件的方法&#xff0c;并分析它们的优缺点…

数据结构学习之顺序栈应用的案例(有效的括号)

实例要求&#xff1a; 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效&#xff1b; 有效字符串需满足的条件&#xff1a; 1、左括号必须用相同类型的右括号闭合&#xff1b; 2、左括号必须…

robot_sim配置

robot_sim https://github.com/Suyixiu/robot_sim 的issue https://www.bilibili.com/video/BV19f4y1h73E/ 下评论 文件来源 其余克隆 基于几何的抓取 banana不显示 解决参考 https://github.com/Suyixiu/robot_sim/issues/2 &#xff08;项目配置主要参考&#xff09; …

LabVIEW在动态力传感器校准技术的创新应用

简介 动态力传感器校准装置集成了冲击法原理和自动化控制&#xff0c;实现精准、高效的传感器校验。LabVIEW的图形化界面提供简便操作和实时数据分析&#xff0c;显著提高了校准过程的准确度和效率。 01 系统设计和功能 动态力传感器在工业生产中发挥着重要作用&#xff0c;…