Linux->进程终止和等待

news2025/1/11 21:52:53

目录

1. 进程终止场景

1.1 进程退出码

1.2 进程常见退出方式

 2. 进程等待

2.1 进程等待的必要性

2.2 进程等待的方式

wait()方式

waitpid()方式

options参数

status参数


1. 进程终止场景

代码运行完毕,结果正确
代码运行完毕,结果不正确
代码异常终止

         进程终止也就是我们通常理解的进程退出了,但是我们又怎样理解上面的三句话呢?什么叫做代码运行完毕结果正确或者不正确?难道是我们每一次都需要自己去调试?有或则是每一个都需要通过IO接口,将程序结果调试出来然后在判断程序是否正确?答案很明显不是的,往下将会给你结果。

1.1 进程退出码

        首先,咱们得明白一个概念,判断是一个代码运行完毕结果是否正确,肯定不会是我们之前所想的那样,将程序以printf、cout或者调试的方法用认为的方式判断是否正确,因为对于计算机而言,我们判断是否正确根本没有意义,因为它的运行速度太快了,如果有其它进程需要这个进程的退出信息,难道它会等我们告知一个答案吗?不会的。所以编程语言出现了——进程退出码

        大家第一次看到进程退出码可能会认为这是一个很厉害,很牛逼的东西,其实不是,我们平时练习C语言和C++时都有用到它,只是大伙不知道到底是干嘛的罢了。如下:

 int main()
 {
      return 0;                                                                                                                  
 } 

        上方代码的0就是进程退出码之一,表示程序正确执行,是否感到了一丝丝无语伙伴们,是不是以为是什么呢?哈哈。不过无语归无语,这个知识点却是很多知识点的桥梁,还是得认真了解的。

        0表示程序执行完了结果正确,其余数字都表示结果不正确,为什么?就因为C语言规定了?这玩意你不说谁会懂?我举一个例子:

        假如你和你女朋友说话,你女朋友问你“你爱不爱我”,你说“0”,然后你女朋友就觉得莫名其妙,然后再问你“你是不是有病?我问你话呢”,然后你又回了“3”,你女朋友气不过,她认为你在耍她,最后就气跑了。你冤不冤枉,你有好好回答哇,只是她不懂哇。

        上面这个故事就表明了,我不管你是啥,我问你东西,你就必须得用我知道的方式来回答,否则我生气,然后把你踢了。所以对于计算机而言,我们就是这个女朋友,他需要用我们知道的方式来回答,所以string.h库中有一个strerror函数,可以将我们的进程退出码的信息以文字方式表示出来。如下:

  1 #include<stdio.h>  
  2 #include<stdlib.h>  
  3 #include<sys/types.h>  
  4 #include<string.h>  
  5 #include<unistd.h>  
  6 
  7 int main()
  8 {
  9   int i = 0;
 10   for(i = 0;i<130;++i)
 11   {
 12     printf("我的进程退出码是:%d,退出信息是:%s\n",i,strerror(i));
 13   }
 14 
 15   return 0;
 16 }

         上述为进程退出码的部分信息,可以看到0的退出信息是Success,我没骗人吧。我相信大家好好看一下是能看出某些信息自己在写Linux时是看到过的,比如说退出码2,退出码13。我以退出码为例,如下:

         看到了没?知识串起来了,以前只是听过Linux是用C语言写的,但是不知道是怎么写的,但是这里有没有感觉?

1.2 进程常见退出方式

1. 从main返回
2. 调用exit
3. _exit

         进程退出从main返回的意思就是通过return返回,注意,只有main函数的返回值才被称为进程退出,其它函数的return只是表示这个函数的功能结束了,并带回了一个返回值,接收到这个返回值的地方依旧是这个进程。

        而我们的exit和_exit函数则没有这个限制了,它们能够在任意函数结束进程,使用方法如下:

exit("退出码");

_exit("退出码");

        这样看起来,好像exit函数和_exit函数没有什么区别呢? 

        在大多数情况下,这两个函数并没有什么区别,但是还是有一定的区别的,请先看下图:

         区别就是_exit是一个系统接口,而exit是stdio.h库提供的一个库函数,并且exit函数内部调用的_exit函数,且exit函数调用没有_exit函数暴力,它会先将前面执行的代码运行完了才结束进程,而_exit会直接退出进程,不管缓冲区的数据等

 int main()                                                                                                        
 {
      printf("hello world");
      exit(0);                                                                                                                                   }   

int main()

{

        printf("hello world");
        _exit(0);     
}

 exit();运行结果:

 _exit()运行结果:

 2. 进程等待

        进程等待一般出现在父子进程之间,父进程等待子进程运行完毕,父进程在执行自己的代码。一般是在网络通信连接时有用。

2.1 进程等待的必要性

1. 子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
2. 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
3. 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
4. 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

         上述的概念已经为我们清晰的解释了进程等待的必要性,关于僵尸进程就是子进程退出,但是父进程没有对它的资源做回收,也就是获取进程退出信息,那么子进程的进程状态就会变为Z,并且一直保持这个状态,没有谁能干掉它,它就一直占用系统资源

2.2 进程等待的方式

wait()方式

        wait()是一个在sys/wait.h库里面的函数,函数声明为:

pid_t wait(int*status);

         返回值正确为被等待进程pid,不正确为-1,status参数为输出型参数,当我们不关心子进程的退出状态,那么就可以将其设置能为NULL。如下:

代码:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/wait.h>
  4 #include<sys/types.h>
  5 #include<stdlib.h>
  6 
  7 void test1()
  8 {
  9   int cnt = 5;
 10   while(1)
 11   {
 12     printf("我是子进程,我还能活%dS\n",cnt);
 13     sleep(1);
 14     --cnt;
 15     if(cnt == 0)
 16       exit(110);
 17   }
 18 }
 19 
 20 
 21 int main()
 22 {
 23   pid_t id = fork();
 24   if(id == 0)
 25   {                                                                                                                          
 26     test1();
 27   }
 28   pid_t subid = wait(NULL);
 29   printf("我是父进程,我等待子进程%d完毕\n",subid);
 30 
 31   return 0;
 32 }

结果:

         从代码中能够看到父进程的printf语句在子进程被执行完毕之后才执行了,也就表示父进程使用wait函数之后确实处于等待状态,也回收了子进程的信息,因为wait返回了正确的pid。

waitpid()方式

        waitpid()是在sys/types.h库和sys/wait.h库中的函数,声明为:

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函数基本一致,但是它的参数options、和两个函数都有的status输出型参数我需要为大家讲解一下。

options参数

        当我们将其输入为0时,此时函数的功能就是进程等待模式,也就是上述图中的模式,但是如果我们将其输入为WNOHANG,那么函数的功能就会变为轮询式等待,什么意思呢,先看代码:

    1 #include<stdio.h>
    2 #include<unistd.h>
    3 #include<sys/wait.h>
    4 #include<sys/types.h>
    5 #include<stdlib.h>
    6 
    7 void test1()
    8 {
    9   int cnt = 5;
   10   while(1)
   11   {
   12       printf("我是子进程,我还能活%dS\n",cnt);
   13     sleep(1);
   14     --cnt;
   15     if(cnt == 0)
   16       exit(110);
   17   }
   18 }
   19 
   20 
   21 int main()
   22 {
   23   pid_t id = fork();
   24   if(id == 0)
   25   {                                                                                                                        
   26     test1();
   27   }
   28   int status = 0;
   29   pid_t subid = waitpid(id,&status,WNOHANG);
   30   printf("我是父进程,我不想等子进程了,先运行了,之后在回收资源\n");
   31   return 0;
   32 }

         上图中可以看出,我们通过WNOHANG参数,父进程就没有等到子进程执行完了再执行代码,而是先运行着自己的代码,子进程自己跑。我将一个故事,大家大概就能懂了。

        小明和小芳是一对好朋友,一天小明想要邀请小芳一起出去玩,然后呢,他就打电话给小芳问她现在能不能出去?小芳说:等等我,我在洗澡呢,小明说:好,我等你,电话别挂。然后电话就一直打着,15分钟后,小芳说:我好了。然后挂断了电话,小明和小芳就一起出去了。——————进程等待

         还是小明和小芳,同样小明想要邀请小芳出去玩,小芳也还在洗澡,小明打电话问:还在洗吗?小芳说:嗯。然后电话挂了,小明洗了个脸,大概一分钟又问:还在洗?小芳说:嗯。如此循环,直到小芳说出洗完了,才不会继续打电话问了。——————轮询式等待

         上述的两个故事就对应了我们进程的等待方式。小明就是父进程、小芳就是子进程。

status参数

         什么叫做输出型参数呢?输出型参数就是指,我们只需要传一个地址进入就行,然后其它函数拿到这一个空间,对其进行数值修改,在原函数就能拿到这个值了。

        status是一个整型变量,他也是一个位图,意思就是他用一个变量存了进程退出码和异常信息的值,它的高16位没有被使用,次高8位用于存进程退出码,低7位用于存异常信息,还有第8位用于存core dump信息。如下:

printf("我是父进程,子进程的退出码为%d,异常信息为%d\n",(status>>8)&0xFF,status&0x7F);  

        该方式就是我们的位图,使用方式就是位运算。 

        值得一提的是,当子进程有一个错误,那么就不会有退出码,也就是说——程序无异常->程序执行是否正确

n /= 0;   错误语句

         程序崩溃,子进程没有成功运行,退出码为0,异常信息为8。


        以上就是我对进程终止和进程等待的全部理解了,还请大家多多支持哇,谢谢。

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

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

相关文章

【编程架构实践】关于技术栈和架构

架构是什么&#xff1f;老生常谈了。那就看看ChatGPT怎么说&#xff1a;软件架构是软件工程师在设计一个软件系统时&#xff0c;定义系统架构结构的一种科学方法。它指的是软件系统在软件工程师关注功能、性能和安全等质量属性的条件下&#xff0c;组织系统的方式。换句话说&am…

Flask源码篇:wsgi、Werkzeug与Flask启动工作流程

目录1 wsgi介绍2 使用wsgi实现一个web应用3 Werkzeug介绍4 Flask工作流程分析&#xff08;1&#xff09;创建Flask app&#xff08;2&#xff09;启动Falsk app&#xff08;3&#xff09;分析run_simple方法&#xff08;4&#xff09;分析make_server方法&#xff08;5&#xf…

内容分发网络

介绍 CDN 内容分发网络&#xff08;英语&#xff1a;Content Delivery Network 或 Content Distribution Network&#xff0c;缩写&#xff1a;CDN&#xff09;是建立并覆盖在承载网上&#xff0c;由不同区域的服务器组成的分布式网络。将源站资源缓存到全国各地的边缘服务器&…

【数据库视图】简单学习视图,了解一些视图的简单功能

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#x1f345;&#x1f345;&#x1f345;&#xff0c;今天我想带大家去了解一下数据库的视图,虽然视图这个东西在很多地方(各种公司以及项目)已经不再用了,但是许多大学生在考试的时候涉及,&#x1f6f4;&#x1f6f4;&#x1f…

【c++】STL常用容器5—list容器

文章目录list基本概念list构造函数list赋值和交换list大小操作list插入和删除list数据存取list反转和排序list基本概念 功能&#xff1a;将数据进行链式存储。 链表&#xff08;list&#xff09;是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链…

京东物流实时风控实践

摘要&#xff1a;本文整理自京东风控数据产品组架构师周文跃&#xff0c;在 FFA 2022 实时风控专场的分享。本篇内容主要分为六个部分&#xff1a;1. 京东物流业务介绍2. 物流风控场景概括3. 物流风控平台建设4. Flink 赋能5. 技术挑战6. 未来规划Tips&#xff1a;点击「阅读原…

操作系统权限提升(二十)之Linux提权-计划任务提权

系列文章 操作系统权限提升(十八)之Linux提权-内核提权 操作系统权限提升(十九)之Linux提权-SUID提权 计划任务提权 计划任务提权原理 linux计划任务提权是因为权限配置不当&#xff0c;计划任务以root权限运行&#xff0c;低权限的用户可以修改计划任务的文件&#xff0c;…

docker启动容器服务之后访问失败

关于docker启动容器服务之后&#xff0c;宿主机访问失败&#xff08;解决方法&#xff09; 注&#xff1a;在进行docker容器启动宿主机进行容器访问时&#xff0c;无需进行网络的配置&#xff0c;docker容器在启动时会自动解决 第一种原因及修改方法 在进行启动的时候&#…

JVM虚拟机概述(1)

1.JVM概述 1.1为什么要学习JVM 通过学习JVM ( java Virtual Machine )可以帮助我们理解java程序运行的过程&#xff0c;了解虚拟机中各种机制的实现原理。为后期写出优质的代码做好准备&#xff0c;为向更高的层次提升打好基础。 1.2虚拟机 虚拟机的本质就是在windows中&…

深入浅出的学习傅里叶变换

学习傅里叶变换需要面对大量的数学公式&#xff0c;数学功底较差的同学听到傅里叶变换就头疼。事实上&#xff0c;许多数学功底好的数字信号处理专业的同学也不一定理解傅里叶变换的真实含义&#xff0c;不能做到学以致用&#xff01; 事实上&#xff0c;傅里叶变换的相关运算…

敏捷-期末

什么是敏捷开发&#xff1f; 敏捷开发(Agile Development)是一种以人为核心、迭代、循序渐进的开发方法。 怎么理解呢&#xff1f;它不是一门技术&#xff0c;它是一种开发方法&#xff0c;也就是一种软件开发的流程&#xff0c;它会指导我们用规定的环节去一步一步完成项目的开…

阿里云服务器ECS的功能特性有哪些?

本文介绍云服务器ECS的功能特性&#xff0c;帮助您更好地了解和使用云服务器ECS。 1、实例 实例是云上的虚拟计算服务器&#xff0c;内含vCPU、内存、操作系统、网络、磁盘等基础组件。您可以使用阿里云提供的控制台、API等管理工具创建和管理ECS实例&#xff0c;像使用本地服…

常用的 JVM 参数(第三章)

《实战Java虚拟机&#xff1a;JVM故障诊断与性能优化 (第2版)》 第三章 常用的 JVM 参数 3.1. 掌握跟踪调试参数 - 一切运行都有迹可循 参数类型参数作用备注GC 参数-JVM 提供了一些跟踪系统状态的参数&#xff0c;使用给定的参数执行 JVM&#xff0c;就可以在系统运行时打印…

基于ssm框架实现家庭理财收支系统(源码+数据库+文档)

一、项目简介 本项目是一套基于ssm框架实现家庭理财收支系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c…

【谷粒学院】微信扫码登录(199~206)

199.OAuth2介绍 OAuth2是什么&#xff1f; OAuth2是针对特定问题的一种解决方案 主要可以解决两个问题&#xff1a;开放系统间授权、分布式访问问题 一、OAuth2解决什么问题 1、OAuth2提出的背景 照片拥有者想要在云冲印服务上打印照片&#xff0c;云冲印服务需要访问云存储服…

_Linux (HTTP协议)

文章目录1. 认识URL2. urlencode和urldecode3. HTTP协议格式3-1. HTTP请求3-1. HTTP响应4. HTTP的方法5. HTTP的状态码6. TTP常见Header7. 最简单的HTTP服务器虽然我们说, 应用层协议是我们程序猿自己定的但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我…

Java基础:JDK7-时间Date

JDK7以前时间相关类 1.Date Date date new Date(); , sout(date)得到的是现在所处位置的时间 Date date new Date(0L); , sout(date)得到的是时间原点也就是1970年1月1日08:00(东八区). date.setTime(1000L); sout(date)得到的是时间原点后一秒钟的时间 long time date.g…

【matplotlib】可视化解决方案——如何设置matplotlib风格集

概述 一般情况下&#xff0c;为了获得数据可视化展示效果&#xff0c;在代码实现的尾部会调用 plt.show()&#xff0c;这种情况下会调用默认的绘图风格&#xff0c;即白色底色&#xff0c;字体和线条呈现黑色。在某些情况下会显得很不协调。如果想要将展示效果以一个固定的风格…

华为OD机试用Python实现 -【称砝码】(2023-2-26华为OD机试人员抽到的真题)

华为OD机试题 华为 OD 机试 300 题大纲称砝码题目输入输出示例一输入输出说明:Python 代码展示编码思路华为 OD 机试 300 题大纲 参加华为 od 机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.csdn.net/hihell/categor…

【JavaSE】集合(Map)

文章目录1.Map接口1.1常用方法1.2 遍历2.HashMap2.1Hash表数据结构2.2 Map.put实现原理2.3 Map.get实现原理2.4扩容2.5 同时重写hashCode和equals2.6 JAVA8对HashMap改进3.Hashtable4.Properties5.TreeMap5.1 概述5.2 自定义类实现Comparable6.集合工具类Collections1.Map接口 …