【Linux】进程等待

Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法...感兴趣就关注我吧!你定不会失望。

本篇导航

  • [0. 进程等待概念](#0. 进程等待概念)
  • [1. wait()函数解析](#1. wait()函数解析)
  • [2. waitpid() 函数解析](#2. waitpid() 函数解析)
    • [2.1 pid_t pid](#2.1 pid_t pid)
    • [2.2 int * wstatus](#2.2 int * wstatus)
    • [2.3 int options](#2.3 int options)
  • 3.Task_struct中的退出状态信息

0. 进程等待概念

一个进程的退出码关乎这个进程的运行状态.那我们如何获取子进程运行状态,这就是进程等待的作用.

  • 什么是进程等待:

通过系统调用wait()||waitpid()来对子进程进行状态检测与回收功能.

  • 为什么我们需要进程等待这一功能呢?
    1. 我们有时需要关注这个进程的退出状态
    2. 当子进程结束为僵尸进程的时候,我们需要父进程去等待回收这个子进程.否则会造成内存泄漏的问题

1. wait()函数解析

在man手册里可以看到其参数为int * status.我们先不传入参数.先这样使用

cpp 复制代码
pid_t ret=wait(NULL);

若回收成功则返回捕获回收成功的子进程ID.若回收失败则返回-1

我们先试着这样使用,编译运行以下程序.

cpp 复制代码
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
void runchild()
{
    int cnt = 5;
    while(cnt--)
    {
      printf("i am a child process my pid is : %d,my parent is: %d,cnt :%d\n",getpid(),getppid(),cnt);
        sleep(1);
    }
}
int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        runchild();
        exit(0);
    }
    else if(id < 0)
    {
        printf("create faild\n");
    }
    else
    {
        sleep(10);
        pid_t ret = wait(NULL);
        if(ret == id)
        {
            printf("wait success!\n");
        }
        sleep(10);
    }
	return 0;
}

我们可以观察到该进程的每一个状态:

  • 首先,父子进程被创建,子进程打印相关信息.

  • 五秒后子进程退出为僵尸状态.父进程还未回收子进程

  • 十秒过后,父进程回收完毕子进程

  • 父进程退出

父进程在等待回收子进程时,不回做接下去的事情.这就是阻塞调用.整个进程都在等待子进程回收的完成

2. waitpid() 函数解析

waitpid相较于wait多了两个参数.

  1. pid_t pid 为需要等待的进程pid
  2. int options 为是否需要进行阻塞调用
  3. *int wstatus 为进程退出码与退出状态

从上到下我们一个个来解析.

2.1 pid_t pid

我们传入-1,则表示等待任意一个子进程.传入固定的pid,则为等待固定的子进程.

cpp 复制代码
waitpid(-1,NULL,0);

此时的waitpid函数与我们上面使用的wait函数等价.

2.2 int * wstatus

之前我们提到过了,我们需要把一个子进程的退出状态(退出码,退出信号)带出来.如何从一个函数中带出一个变量呢?

除了返回值,我们可以使用指针带出.

cpp 复制代码
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
void runchild()
{
    int cnt = 3;
    while(cnt--)
    {
        printf("i am a child process my pid is : %d,my parent is: %d,cnt :%d\n",getpid(),getppid(),cnt);
        sleep(1);
    }
}
int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        runchild();
        exit(1);
    }
    else if(id < 0)
    {
        printf("create faild\n");
    }
    else
    {
        int status=0;
        pid_t ret = waitpid(-1,&status,0);
        if(ret == id)
        {
            printf("wait success!,status: %d\n",status);
        }
        sleep(10);
    }
    return 0;
}

子进程的退出码是1.而运行这段程序 我们发现获取的退出码是256.这是为什么呢?

status的设计类似bitmap的思想.在一个32位整型数中,我们现阶段只用到低16位

进程结束有三种状态:

  1. 正常终止结果正确
  2. 正常终止结果错误
  3. 异常终止

第一、二种情况都为正常终止,此时低8位(0-7)为0.从第8位开始保存退出码.

第三种情况位被异常终止,本质为被信号杀死.则其低8位用来表示被杀死的信号类别.

所以我们需要按位与就可以取出对应的退出码和终止信号.

其中sig code= status& 0x7f 提取出低7位

​ exit code = (status>>8) & 0xff 提取出高8位

当然我们系统也提供了三个宏,来判断子进程是否正常退出?退出码以及退出信号

2.3 int options

有一个参数为WNOHANG,非阻塞调用.

也就是父进程只会询问一次,若执行到这时,子进程等待回收,则回收.若无,则继续往下执行.

通常我们可以通过轮询等待(通过循环,每隔一段时间问一次)来提高效率.

没有等待成功的反回值为0

3.Task_struct中的退出状态信息

为什么我们不直接取,要么麻烦通过函数来访问呢?

因为这个退出信息是存在子进程的Task_struct中的.访问系统的数据只能通过系统调用来进行.

在多进程运行时,我们并不知道哪个进程先开始运行,但是一定要保证,父进程是最后一个退出的进程,否则就会出现长时间的僵尸状态,进而导致内存泄漏。

相关推荐
JSU_曾是此间年少11 分钟前
数据结构——线性表与链表
数据结构·c++·算法
duration~39 分钟前
Maven随笔
java·maven
zmgst42 分钟前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
€☞扫地僧☜€1 小时前
docker 拉取MySQL8.0镜像以及安装
运维·数据库·docker·容器
hjjdebug1 小时前
linux 下 signal() 函数的用法,信号类型在哪里定义的?
linux·signal
其乐无涯1 小时前
服务器技术(一)--Linux基础入门
linux·运维·服务器
跃ZHD1 小时前
前后端分离,Jackson,Long精度丢失
java
Diamond技术流1 小时前
从0开始学习Linux——网络配置
linux·运维·网络·学习·安全·centos
写bug的小屁孩1 小时前
前后端交互接口(三)
运维·服务器·数据库·windows·用户界面·qt6.3
斑布斑布1 小时前
【linux学习2】linux基本命令行操作总结
linux·运维·服务器·学习