【Linux】—— 进程等待 waitwaitpid

序言:

 

之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。因此,为了解决这个问题,就需要用到有关 “进程等待” 的基本知识!!!
 


目录

(一)进程的等待必要性

(二)进程等待的方法

1、wait方法

2、waitpid方法

(三)获取子进程status

 1、进程的阻塞等待方式

2、进程的非阻塞等待方式

总结


(一)进程的等待必要性

进程等待通常是指父进程等待子进程的执行完成。当一个进程创建了一个子进程,并希望在子进程执行完成后再继续执行自己的任务时,父进程需要等待子进程的完成。

以下是一些常见的原因和情况,需要进程等待:

  1. 避免僵尸进程:如果父进程不等待子进程完成而直接退出,子进程可能会成为僵尸进程。僵尸进程是已经完成执行但尚未被父进程回收的子进程。为了避免僵尸进程的产生,父进程需要等待子进程完成并回收子进程的资源

  2. 父子进程间通信:在使用fork()系统调用创建子进程时,父进程通常需要等待子进程完成,以确保获取子进程的执行结果或处理子进程的退出状态;

  3. 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
     

因此,我们可以得出进程等待就是:

  • 通过系统调用,获取子进程退出码或者退出信号的方式,顺便释放内存问题

(二)进程等待的方法

进程等待可以通过操作系统提供的函数来实现,主要有以下两种方法来进程等待操作:

  • waitpid函数:等待指定的子进程完成。可以设置选项参数来指定等待条件。
  • wait函数:等待任意一个子进程完成并获取其退出状态。

1、wait方法

遇到新的函数,如果不认识,就先去系统中查询一下:

 【说明】

  1. 返回值:成功返回被等待进程pid,失败返回-1。
  2. 参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
     

接下来,通过代码简单的

 int main()
 {
     pid_t id = fork();
     if(id == 0)
     {
         //子进程
         int cnt = 5;
        while(cnt)
         {
             printf("我是子进程,我还活着呢,我还有%dS, pid: %d, ppid%d\n", cnt    --, getpid(), getppid());
             sleep(1);
 
         }
         exit(0);
     }
     sleep(10);

      // 父进程
      pid_t ret_id = wait(NULL);
      printf("我是父进程,等待子进程成功, pid: %d, ppid: %d, ret_id: %d\n",getp    id(), getppid(), ret_id);    
                                                 
      sleep(5);
 }

输出显示:

【解释】

  • 上述代码是一个简单的示例,展示了父进程创建子进程,并使用wait函数等待子进程完成。

进阶着又有一个问题:那就是父进程在 wait的时候,如果子进程没有退出,那么父进程在干什么呢?

我们把代码改一下看输出结果。代码如下:

 int main()
 {
     pid_t id = fork();
     if(id == 0)
     {
         //子进程
         int cnt = 5;
        while(cnt)
         {
             printf("我是子进程,我还活着呢,我还有%dS, pid: %d, ppid%d\n", cnt    --, getpid(), getppid());
             sleep(1);
 
         }
         exit(0);
     }
 

      // 父进程
      pid_t ret_id = wait(NULL);
      printf("我是父进程,等待子进程成功, pid: %d, ppid: %d, ret_id: %d\n",getp    id(), getppid(), ret_id);    
                                                 
      sleep(5);
 }

输出显示:


2、waitpid方法

wait 和 waitpid 的头文件一样。具体如下:

pid_ t waitpid(pid_t pid, int *status, int options);


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

  1. 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  2. 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  3. 如果不存在该子进程,则立即出错返回。
     

(三)获取子进程status

  1. wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  2. 如果传递NULL,表示不关心子进程的退出状态信息。
  3. 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  4. status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位)

具体可以向下述这样理解: 

 

 

判断一个进程是否正常退出,只需要判断是否接收到进程终止信号大于0,异常退出,有进程终止信号;等于0,正常退出。 

代码演示:

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
         //子进程
         int cnt = 5;
         while(cnt)
         {
             printf("我是子进程,我还活着呢,我还有%dS, pid: %d, ppid: %d\n", cnt--, getpid(), getppid());
             sleep(1);
 
         }
        exit(111);
     }                                                                                                                          
  
     // 父进程
    int status =0 ;
 
   
    pid_t ret_id = waitpid(id, &status, 0);
      printf("我是父进程,等待子进程成功, pid: %d, ppid: %d, ret_id: %d,child exit code: %d, child exit signal: %d\n"\
          ,getpid(), getppid(), ret_id,(status>>8)&0xFF, status & 0x7F);
      sleep(2);
}

输出结果:


 1、进程的阻塞等待方式

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
    pid_t pid;
    pid = fork();
    if(pid < 0){
    printf("%s fork error\n",__FUNCTION__);
    return 1;
    } else if( pid == 0 ){ //child
    printf("child is run, pid is : %d\n",getpid());
    sleep(5);
    exit(257);
    } else{
        int status = 0;
        pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5S
        printf("this is test for wait\n");
        if( WIFEXITED(status) && ret == pid ){
        printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
        }else{
            printf("wait child failed, return.\n");
            return 1;
        }
    }
    return 0;
}

输出展示:

 此运行结果是子进程正常退出的场景

2、进程的非阻塞等待方式

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 
  7 int main()
  8 {
  9   pid_t pid;
 10   pid = fork();
 11   if(pid < 0){
 12     printf("%s fork error\n",__FUNCTION__);
 13     return 1;
 14   }else if( pid == 0 ){ //child
 15       printf("child is run, pid is : %d\n",getpid());
 16       sleep(5);
 17       exit(1);
 18     } else{
 19       int status = 0;
 20       pid_t ret = 0;
 21       do
 22       {
 23         ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
 24         if( ret == 0 ){
 25         printf("child is running\n");
 26       }
 27       sleep(1);
 28       }while(ret == 0);
 29       if( WIFEXITED(status) && ret == pid ){
 30       printf("wait child 5s success, child return code is :%d.\n",WEXITS    TATUS(status));
 31       }else{
 32         printf("wait child failed, return.\n");
 33         return 1;
 34       }
 35     }                                                                   
 36   return 0;
 37 }
 38 

输出展示:

调用 waitpid 函数(非阻塞,WNOHANG)父子进程交替打印进行各自的循环,大概 5s 之后子进程, 可以看到子进程由 S+ 变成 Z+。

若WIFEXITED(status)为真---->进程正常退出

若WEXITSTATUS(status)>0---->WEXITSTATUS(status)表示子进程的退出码


总结

以上便是关于进程等待的全部知识了。感谢大家的观看与支持!!!

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

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

相关文章

【沁恒蓝牙mesh】CH58x flash分区之利用随机数作为蓝牙mesh地址

本文主要介绍了 沁恒蓝牙芯片 CH58x 的flash 分区与数据存储管理&#xff0c;利用随机数作为蓝牙mesh地址&#xff0c;蓝牙mesh采用自组网 &#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是喜欢记录零碎知识点的小菜鸟。&#x1f60e;&#…

python实现简单的爬虫功能

前言 Python是一种广泛应用于爬虫的高级编程语言&#xff0c;它提供了许多强大的库和框架&#xff0c;可以轻松地创建自己的爬虫程序。在本文中&#xff0c;我们将介绍如何使用Python实现简单的爬虫功能&#xff0c;并提供相关的代码实例。 如何实现简单的爬虫 1. 导入必要的…

【网络编程】利用套接字实现一个简单的网络通信(UDP实现聊天室 附上源码)

网络编程套接字 &#x1f41b;预备知识&#x1f98b;理解源IP地址和目的IP地址&#x1f40c;认识端口号&#x1f41e; 理解 "端口号" 和 "进程ID"&#x1f41c;简单认识TCP协议&#x1f99f;简单认识UDP协议&#x1f997; 什么是网络字节序 &#x1f577;相…

《剑指offer》(5)搜索算法、位运算、模拟

方法一&#xff1a; class Solution: def GetNumberOfK(self , nums: List[int], k: int) -> int: #从两边开始找&#xff0c;找到之后记录当前位置 left 0 right len(nums) - 1 if k not in nums: return 0 start len(nums) - 1 end 0 while left < right: if nums…

Vue2源码分析-day1

初始化数据 vue中最核心的我们都知道那就是响应式数据&#xff0c;数据的变化视图自动更新。那么我们来new一个我们自己的vue 在index.html文件下加入如下代码&#xff0c;这也是vue最常见的基本结构。data已经有了下面我们来获取data的数据 <script src"./vue.js&qu…

[openCV]基于拟合中线的智能车巡线方案V3

import cv2 as cv import os import numpy as np# 遍历文件夹函数 def getFileList(dir, Filelist, extNone):"""获取文件夹及其子文件夹中文件列表输入 dir&#xff1a;文件夹根目录输入 ext: 扩展名返回&#xff1a; 文件路径列表"""newDir d…

『Samba』在Linux中实现高效访问和管理共享文件夹的基本操作与实践

&#x1f4e3;读完这篇文章里你能收获到 Samba 的安装和配置&#xff1a;详细介绍了如何在 Linux 操作系统上安装和配置 Samba 服务器共享文件夹的设置&#xff1a;指导如何选择要共享的文件夹&#xff0c;并为其设置共享名称、路径以及访问权限Samba 用户的创建&#xff1a;提…

C# App.config和Web.config加密

步骤1&#xff1a;创建加密命令 使用ASP.NET提供的命令工具aspnet_regiis来创建加密命令。 1、打开控制台窗口&#xff0c;在命令行中输入以下命令&#xff1a; cd C:\Windows\Microsoft.NET\Framework\v4.xxxxx aspnet_regiis.exe -pef connectionStrings "C:\MyAppFo…

搭建 elasticsearch8.8.2 伪集群 windows

下载windows 版本 elasticsearch8.8.2 以下链接为es 历史版本下载地址&#xff1a; Past Releases of Elastic Stack Software | Elastic windows 单节点建立方案&#xff1a; 下载安装包 elasticsearch-8.8.2-windows-x86_64.zip https://artifacts.elastic.co/download…

代码随想录算法训练营第51天|动态规划part09|198.打家劫舍、213.打家劫舍II、337.打家劫舍III

代码随想录算法训练营第51天&#xff5c;动态规划part09&#xff5c;198.打家劫舍、213.打家劫舍II、337.打家劫舍III 198.打家劫舍 198.打家劫舍 思路&#xff1a; 仔细一想&#xff0c;当前房屋偷与不偷取决于 前一个房屋和前两个房屋是否被偷了。 所以这里就更感觉到&a…

机器学习鱼书笔记(自用更新)

零、预知识 1.Numpy 使用 介绍&#xff1a;高效的操作多维数组的函数库。 安装&#xff1a;&#xff08;前提已经安装了python&#xff09; pip install numpy导入 import numpy as np创建数组 Numpy最重要的数据结构是多维数组&#xff08;ndarray&#xff09;。通过Numpy&…

农商行基于分类分级的数据安全管控建设实践

《数据安全法》颁布实施以来&#xff0c;以分类分级为基础&#xff0c;对数据进行差异化管理和防护&#xff0c;成为行业共识。 金融行业作为数据密集的高地&#xff0c;安全是重中之重&#xff0c;而鉴于金融数据种类和内容庞杂&#xff0c;面临规模化用数、普惠用数、跨机构共…

分布式协议与算法——Paxos算法

目录 Paxos算法Basic Paxos算法三种角色如何达成共识&#xff08;协商过程&#xff09;小结&#xff1a; Multi-Paxos算法关于 Multi-Paxos 的思考领导者优化Basic PaxosChubby 的 Multi-Paxos 实现小结 参考 Paxos算法 Paxos论文 Paxos Made Simple 、author&#xff1a;Lesli…

wireshark 安装和使用

wireshark&#xff0c;世界上最受欢迎的网络协议分析器。是一个网络流量分析器&#xff0c;或“嗅探器”&#xff0c;适用于Linux、macOS、*BSD和其他Unix和类Unix操作系统以及Windows。它使用图形用户界面库Qt以及libpcap和npcap作为数据包捕获和过滤库。 wireshark&#xff…

Flamingo

基于已有的图像模型和文本模型构建多模态模型。输入是图像、视频和文本&#xff0c;输出是文本。 Vision encoder来自预训练的NormalizerFree ResNet (NFNet)&#xff0c;之后经过图文对比损失学习。图片经过图像模型的输出是2D grid&#xff0c;视频按1FPS的频率采样后经过图…

【2种方法,jmeter用一个正则提取器提取多个值!】

jmeter中&#xff0c;用json提取器&#xff0c;一次提取多个值&#xff0c;这个很多人都会。但是&#xff0c;用正则提取器一次提取多个&#xff0c;是否可以呢&#xff1f; 肯定&#xff0c;很多人都自信满满的说&#xff0c;可以&#xff01;形如&#xff1a;token":&q…

Python入门【​编辑、组合、设计模式_工厂模式实现 、设计模式_单例模式实现、工厂和单例模式结合、异常是什么?异常的解决思路 】(十七)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…

matlab使用教程(10)—脚本和函数

1.概述 MATLAB 提供了一个强大的编程语言和交互式计算环境。您可以使用此语言在 MATLAB 命令行中一次输入一个命令&#xff0c;也可以向某个文件写入一系列命令&#xff0c;按照执行任何 MATLAB 函数的相同方式来执行这些命令。使用 MATLAB 编辑器或任何其他文件编辑器可以创建…

使用HTTP隧道时如何应对目标网站的反爬虫监测?

在进行网络抓取时&#xff0c;我们常常会遇到目标网站对反爬虫的监测和封禁。为了规避这些风险&#xff0c;使用代理IP成为一种常见的方法。然而&#xff0c;如何应对目标网站的反爬虫监测&#xff0c;既能保证数据的稳定性&#xff0c;又能确保抓取过程的安全性呢&#xff1f;…

Gartner发布《2023年全球RPA魔力象限》:90%RPA厂商,将提供生成式AI自动化

8月3日&#xff0c;全球著名咨询调查机构Gartner发布了《2023年全球RPA魔力象限》&#xff0c;通过产品能力、技术创新、市场影响力等维度&#xff0c;对全球16家卓越RPA厂商进行了深度评估。 弘玑Cyclone&#xff08;Cyclone Robotics&#xff09;、来也&#xff08;Laiye&am…