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

news2025/4/6 6:56:46

一、进程的终止

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'的,则表示他后面的命令行参数是以数组的形式传递。

以此类推即可。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习笔记-数据库概念介绍

一.数据库概述 1.数据库: 存储数据的仓库,本质是一个文件系统. 用户可以对数据库中的数据进行 增加,修改,删除以及查询操作 2.特点 可以结构化存储大量的数据可以有效的保持数据的一致性,完整性读写效率高 3.常用数据库 二.数据库分类及其常用 关系型数据库 指的是二维表格…

渗透测试:保障网络安全的重要手段!

随着信息技术的快速发展&#xff0c;网络安全问题日益受到关注。渗透测试作为一种重要的网络安全检测和评估方法&#xff0c;已经成为保障网络安全的重要手段之一。本文将介绍渗透测试的基本概念、流程、以及在保障网络安全方面的应用。 一、渗透测试的基本概念 渗透测试是一…

linux 服务器上安装 ftp(亲测有效)

目录 1 需求2 安装 1 需求 服务器上需要安装ftp 2 安装 1 下载地址 https://developer.aliyun.com/packageSearch?wordvsftpd或者 https://rpmfind.net/linux/rpm2html/search.php?queryvsftpd2 如何判断 服务器是否安装了ftp rpm -qa | grep vsftpd出现提示则表示已安装…

【从0上手cornerstone3D】如何渲染一个基础的Dicom文件(含演示)

一、Cornerstone3D 是什么&#xff1f; Cornerstone3D官网&#xff1a;https://www.cornerstonejs.org/ 在线查看显示效果&#xff08;加载需时间&#xff0c;可先点击运行&#xff09;&#xff0c;欢迎fork 二、代码示例 了解了Cornerstone是什么&#xff0c;有什么作用后&…

解决elementUI或elementPlus的按钮点击后需要失去焦点才能恢复原本样式问题

废话不多说直接上代码&#xff0c;只需要在button中添加如下代码即可 focus"(e) > e.target.blur()"

精确掌控并发:分布式环境下并发流量控制的设计与实现(二)

3. 固定窗口 参考&#xff1a;精确掌控并发&#xff1a;分布式环境下并发流量控制的设计与实现&#xff08;一&#xff09;-CSDN博客 4. 滑动窗口 滑动窗口算法是一种更为灵活的流量控制方案&#xff0c;它比固定窗口算法能更平滑地处理突发流量。在滑动窗口中&#xff0c;时…

什么是云服务器ECS及其优势、购买、使用方式和部署建议

阿里云服务器ECS英文全程Elastic Compute Service&#xff0c;云服务器ECS是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;阿里云提供多种云服务器ECS实例规格&#xff0c;如经济型e实例、通用算力型u1、ECS计算型c7、通用型g7、GPU实例等&#xff0c;阿里云百科aliyunbai…

UE5 通过接口实现角色描边效果

接口不能够被实例化&#xff0c;不能够在内部书写函数的逻辑和设置属性&#xff0c;只能够被继承使用。它能够让不同的类实现有相同的函数&#xff0c;继承接口的类必须实现接口的函数。 并且&#xff0c;我们可以在不同的类里面的函数实现也不同&#xff0c;比如A类描边是红色…

Windows 项目从0到1的部署

目录 一. 安装jdk 1.1 安装jdk 1.2 配置jdk的环境配置jdk 1.3 配置成功 二. 配置tomcat 2.1 启动tomcat 2.2 防火墙设置 三. 安装MySQL 3.1 安装步骤 3.2 内部连接 3.3 外部连接 四. 部署项目 4.1 项目部署 4.2 修改mysql的用户密码 一. 安装jdk 这里给大家准备好了jdk和…

Chrome DevTools 常用面板攻略

文章目录 前言一、概述1.1 简介1.2 DevTools 初步了解 二、设备模式&#xff08;Device Mode&#xff09;2.1 面板概述2.2 设备切换 三、元素面板&#xff08;Elements&#xff09;3.1 面板概述3.2 编辑样式3.2.1 添加、启用和停用 CSS 类3.2.2 添加或移除动态样式3.2.3 快速向…

YOLOv5改进 | 检测头篇 | ASFFHead自适应空间特征融合检测头(全网首发)

一、本文介绍 本文给大家带来的改进机制是利用ASFF改进YOLOv5的检测头形成新的检测头Detect_ASFF,其主要创新是引入了一种自适应的空间特征融合方式,有效地过滤掉冲突信息,从而增强了尺度不变性。经过我的实验验证,修改后的检测头在所有的检测目标上均有大幅度的涨点效果,…

【数据库原理】(24)数据库安全性策略

数据库安全性是数据库管理系统&#xff08;DBMS&#xff09;中一个至关重要的方面。它指的是保护数据库免受非授权访问和恶意操作&#xff0c;包括数据泄露、修改、破坏等。 多层安全模型 在典型的计算机系统安全模型中&#xff0c;安全措施被设置在不同层级&#xff1a; 应用…

将.NET应用转换成Window服务

写在前面 本文介绍了将.NET8.0应用程序转换成Windows服务。 需要在NuGet中获取并安装&#xff1a;Microsoft.Extensions.Hosting.WindowsServices 包 代码实现 using System.Runtime.InteropServices; using WorkerService1;public class Program {public static void Main…

Vue+ElementUI+Axios实现携带参数的文件上传(数据校验+进度条)

VueElementUIAxios实现携带参数的文件上传&#xff08;数据校验进度条&#xff09; 可以实现对上传文件的类型&#xff0c;大小进行数据校验&#xff0c;以及对上传文件所要携带的数据也进行的校验&#xff0c;也有文件上传进度的进度条。 一、Vue 结构部分 弹窗显示&#xff0…

Jenkins 问题

从gitlab 仓库拉去代码到Jenkins本地报错 ERROR: Couldn’t find any revision to build. Verify the repository and branch configuration for this job. 问题原因&#xff1a; 创建条目》配置的时候&#xff0c;gitlab仓库不存在master分支 修复后&#xff1a;

[每周一更]-(第82期):认识自然处理语言(NLP)

GPT的大火&#xff0c;带起了行业内大模型的爆发&#xff1b;国内外都开始拥有或者研发自己的大模型&#xff0c;下边我们从NLP来进一步深入了解大模型、AI。 一、什么是NLP&#xff1f; 自然语言处理&#xff08;英语&#xff1a;Natural Language Processing&#xff0c;缩…

2000年第五次人口普查数据,shp/excel格式均有,划分年龄段、性别占比等字段

基本信息. 数据名称: 第五次人口普查数据 数据格式: Shp、excel 数据时间: 2000年 数据几何类型: 面 数据坐标系: WGS84坐标系 数据来源&#xff1a;第五次人口普查数据 数据字段&#xff1a; 序号字段名称字段说明1a2000_zrks2000年_常住人口&#xff08;人&…

Pycharm close project 速度缓慢解决办法

解决Pycharm close project缓慢现象 1.问题描述 close project后需要等待很长的时间。 2.解决办法 在Help -> Find Action -> 输入 Registry -> 禁用ide.await.scope.completion 问题解决&#xff01;&#xff01;&#xff01; &#x1f603;&#x1f603;&#x…

C++(10)——模板

目录 1.什么是泛式编程以及模板的引入&#xff1a; 2. 模板&#xff1a; 2.1 函数模板&#xff1a; 2.2 类模板&#xff1a; 1.什么是泛式编程以及模板的引入&#xff1a; 在之前排序的部分中&#xff0c;为了完成某个特定功能&#xff0c;经常会用到交换函数&#xff0c;即…

Tensorflow2.0笔记 - 修改形状和维度

本次笔记主要使用reshape&#xff0c;transpose&#xff0c;expand_dim&#xff0c;和squeeze对tensor的形状和维度进行操作。 import tensorflow as tf import numpy as nptf.__version__#tensor的shape和维数获取 #假设下面这个tensor表示4张28*28*3的图片 tensor tf.rando…