【Linux】—— 进程等待 wait&&waitpid

简介: 【Linux】—— 进程等待 wait&&waitpid

序言:

 

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

 



(一)进程的等待必要性

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

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

  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)表示子进程的退出码


总结

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

相关文章
|
16天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
43 1
|
4天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
44 13
|
11天前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
19天前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
24天前
|
缓存 算法 Linux
Linux内核的心脏:深入理解进程调度器
本文探讨了Linux操作系统中至关重要的组成部分——进程调度器。通过分析其工作原理、调度算法以及在不同场景下的表现,揭示它是如何高效管理CPU资源,确保系统响应性和公平性的。本文旨在为读者提供一个清晰的视图,了解在多任务环境下,Linux是如何智能地分配处理器时间给各个进程的。
|
1月前
|
网络协议 Linux 虚拟化
如何在 Linux 系统中查看进程的详细信息?
如何在 Linux 系统中查看进程的详细信息?
66 1
|
1月前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
Linux
Linux进程实践(4) --wait避免僵尸进程
Wait的背景    当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止)    子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态。
925 0
|
1月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
95 8
|
1月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
265 6
下一篇
DataWorks