【Linux】进程等待

简介: 【Linux】进程等待

思维导图

学习目标

  • 进程等待的必要性
  • 进程等待的方法
  • wait函数和waitpid函数的学习
  • 参数status的介绍

一、进程等待的必要性

1.1 进程等待是什么?

      任何子进程,在退出的情况下,一般都必须让父进程进行等到,进程在退出的时候,如果父进程不管不顾,自己退出进程,那么子进程会进入僵尸状态Z,会造成内存泄漏

什么是僵尸进程呢??

      代码已经释放,数据也进行了释放,留下了PCB。PCB中会记录这个进程瑞退出时的退出信息。这种状态一直维持,PCB结构体所占的内存时非常大的,所以必然会导致内粗泄露。

1.2 为什么要有进程等待?

  • 父进程通过等待,解决子进程退出的僵尸问题,回收系统资源(一定要考虑的)
  • 获取子进程的退出信息,知道子进程是因为什么原因退出的(可选的功能)

1.3 进程等待的必要性:

  • 子进程退出,父进程如果不管不顾,就可能会导致僵尸进程问题,进而产生内存泄漏
  • 进程一旦变为僵尸状态,基本刀枪不入,kill - 9命令也会无动于衷,因为没有什么方法时可以将一个死去的进程给退出
  • 父进程派给子进程的任务完成的如何,我们需要知道,例如:子进程运行完成,结果是对还是不对。或者子进程有没有正常的退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。

二、进程等待的方法

2.1 获取子进程的status

  • 因为在wait函数和waitpid函数中的参数都有status的参数,该参数是一个输出性参数,由操作系统来填充
  • 如果传递NULL,则不关心子进程的退出状态信息
  • 操作系统可以根据这个status的值,来将子进程的退出信息反馈给父进程
  • status不能简单地当作整数来进行学习,status当作位图来进行表示错误码和错误信息的值是多少。因为只研究低16位,具体细节如下所示:

2.1.1 详细介绍一下位图和status中各个位上的信息:

位图是什么??

      位图(bitset)是一种常见的数据结构,常用在给一个很大范围的数(比如我之前看到一个题是有四十亿个数),判断其中的数是不是在其中。这里就要用到位图,显然这么多得数在内存中是存不下的,我们就得重新想办法。位图在索引、数据压缩有很大的应用。位图使用数组实现的,数组的每一个元素的每一个二进制位都表示一个数据,0表示该数不存在,1表示该数据存在

status各个位上的信息:

      status有32位,但是在这里只用到了16位,从0~6这7位表示的是退出信号的大小,从8~15位这8位表示的是退出码的大小。

2.1.2 通过status计算出错误码和错误信号

int errorcode = status & 0x7f; // 错误信号的计算
int errorsignal = (status >> 8) & 0xff; // 错误码的计算

为了防止使用者将7和8弄混淆,又有专门的宏来表示退出码和推出信号。

  • WIFEXITED(status):用于查看进程是否是正常退出,本质是检查是否收到信号。
  • WEXITSTATUS(status):用于获取进程的退出码。

当一个进程的退出信号出现时,我们不需要关心退出码。

2.2 wait函数

2.2.1 wait函数的用途

      wait函数的用途是等待任何一个子进程的结束。

2.2.2 wait函数的介绍

#include <sys/wait.h>
#include <sys/types.h>
 
pid_t wait(int* status);

返回值:等待成功返回被等待进程的pid,等待失败返回-1。

参数:输出型参数,获取子进程的退出状态,不关心可设置为NULL。

2.2.3 使用代码进行观测

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
 
int main()
{
  printf("I am process, pid: %d, ppid: %d\n", getpid(), getppid());
  pid_t id = fork();
  if(id == 0)
  {
    int cnt = 5;
    while (cnt--)
    {
      sleep(1);
      printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
    }
    exit(0);
  }
 
  printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid());
  pid_t flag = wait(NULL);
  if(flag > 0)
  {
    printf("正常退出!\n");
  }
  sleep(10);
  return 0;
}

我们可以使用以下监控脚本对进程进行实时监控:

while :; do ps ajx | head -1 && ps ajx | grep a.out | grep -v grep; sleep 1; done

      当我们进行运行代码后,我们子进程退出后,父进程会立刻进行回收;之后,让父进程睡眠10秒,那么在监控脚本下,最后就只有父进程。

2.3 waitpid函数

2.3.1 waitpid函数的用途

      waitpid函数的用途是等待父进程的任意一个子进程退出或者等待一个特定的子进程退出。

2.3.2 waitpid函数的介绍

#include <sys/types.h>
#include <sys/wait.h>
 
pid_t waitpid(pid_t pid, int* status, int options);
2.3.2.1 返回值
  • 等待成功返回被等待进程的pid。
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0。
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。
2.3.2.2 参数
  • pid:待等待子进程的pid,若设置为-1,则等待任意子进程。
  • status:输出型参数,获取子进程的退出状态,不关心可设置为NULL。
  • options:当设置为WNOHANG时,若等待的子进程没有结束,则waitpid函数直接返回0,不予以等待。若正常结束,则返回该子进程的pid。

四、父进程的阻塞等待和非阻塞等待

      如果子进程没有退出,父进程在执行waitpid函数进行等待的时候,我们可以将父进程的状态分为阻塞等待和非阻塞等待。阻塞等待就是父进程等待子进程运行完了,父进程再运行,非阻塞等待就是父子进程同时运行。

      阻塞等待:进程阻塞了,父进程在等待某个条件的发生,父进程在等待的过程中什么事情也没有执行。waitpid函数的第三个参数填 0。

      非阻塞等待:waitpid函数的第三个参数填 WNOHANG

函数的返回值有三种情况:

  • 当 pid > 0 时,等待成功,子进程退出,并且父进程回收成功;
  • 当 pid == 0 时,检测是成功的,只不过子进程还未退出,需要进行下一次的重复等待;
  • 当 pid < 0 时,等待失败。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "task.h"
 
typedef void(*func_t)();
 
func_t task[3];
 
void Printlog()
{
  printf("this is Print\n");
}
 
void Download()
{
  printf("this is download\n");
}
 
void MySqlSync()
{
  printf("this is MySqlSync\n");
}
 
void loadtask()
{
  task[0] = Printlog;
  task[1] = Download;
  task[2] = MySqlSync;
}
 
void HanderTask()
{
  int i = 0;
  for(i = 0; i < 3; i++)
  {
    task[i]();
  }
}
 
void DoOtherThing()
{
  HanderTask();
}
 
void Chlidrun()
{
  int cnt = 10;
  while (cnt)
  {
    sleep(1);
    printf("child is doing thing, cnt: %d, pid: %d\n", cnt, getpid());
    cnt--;
  }
}
 
int main()
{
  printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid());
  pid_t id = fork();
  if(id == 0)
  {
    Chlidrun();
    printf("Child quit...\n");
    exit(123);
  }
  
  loadtask();
 
  while (1)
  {
    int status = 0;
    pid_t rid = waitpid(id, &status, WNOHANG);
    if(rid == 0)
    {
      sleep(1);
      printf("child is running, father can do other things\n");
      DoOtherThing();
    }
    else if(rid > 0)
    {
      if(WIFEXITED(status))
      {
        printf("Child quit success, child exit code: %d\n", WEXITSTATUS(status));
      }
      else{
        printf("Child quit failed\n");
      }
      break;
    }
    else 
    {
      printf("waitpid failed\n");
      break;
    }
  }
 
  return 0;
}

   


相关文章
|
14天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
44 4
linux进程管理万字详解!!!
|
5天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
41 8
|
13天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
46 4
|
14天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
16天前
|
消息中间件 存储 Linux
|
22天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
24 1
|
1月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
22 1
|
1月前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
30 0
Linux c/c++之IPC进程间通信
|
1月前
|
Linux C++
Linux c/c++进程间通信(1)
这篇文章介绍了Linux下C/C++进程间通信的几种方式,包括普通文件、文件映射虚拟内存、管道通信(FIFO),并提供了示例代码和标准输入输出设备的应用。
26 0
Linux c/c++进程间通信(1)
|
1月前
|
Linux C++
Linux c/c++进程之僵尸进程和守护进程
这篇文章介绍了Linux系统中僵尸进程和守护进程的概念、产生原因、解决方法以及如何创建守护进程。
20 0