Linux(10)—— 进程控制(等待)

本文主要讨论进程的等待。

进程等待

为什么要进行进程等待?

在前面进程状态中我们知道,子进程退出的时候,父进程如果不做处理,进程就会变为z状态,即僵尸进程,从而导致内存泄漏。 关于进程状态我们已经在前面的文章介绍过:进程状态

而进入僵尸状态的进程不会再被杀死,就算我们输入信号也无法处理这样一个死去的进程。

所以,父进程需要通过进程等待的方式来对进程进行回收,获取子进程的退出信息;也能解决内存泄漏问题。

什么是进程等待?

进程等待是一个通过系统调用 wait/waitpid 来进行对子进程的状态检测与回收的功能。

下面我们利用fork创建子进程,再让子进程结束运行变为僵尸进程:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t id = fork();
    if(id<0)//创建子进程失败
    {
        perror("fork");
        return 1;
    }
    else if(id == 0)
    {
    //child
        int cnt = 5;
        while(cnt)
        {
            printf("child, pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
            cnt--;
            sleep(1);
        }
        exit(0);//子进程退出
    }
    else
    {
    //parent
        while(1)
        {
            printf("father,pid:%d,ppid:%d\n",getpid(),getppid());
            sleep(1);
        }
    }
    return 0;
}

下面我们再打开一个窗口,输入如下命令:

cpp 复制代码
while :; do ps ajx | head -1 && ps ajx | grep test |grep -v grep; sleep 1;echo "-----------------------------------------";done

来同步监视状态的变化。运行上面代码后可以在窗口中看到如下场景:

可以看到在五次循环后,即五秒之后子进程变为僵尸进程状态。

且代码的打印结果为:


wait/waitpid

下面我们介绍如何进行进程等待。

进程等待需要用到wait。我们用man命令查看一下:

bash 复制代码
man 2 wait

其说明为等待一个进程,直到该进程的状态改变。我们先不管函数的参数,在上面的代码例子中,我们修改parent部分的代码,让进程等待实现:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

//...

else
{
//parent
    int val = 10;
    while(val)
    {

        printf("father,pid:%d,ppid:%d,val:%d\n",getpid(),getppid(),val);
        sleep(1);
        val--;
    }
    pid_t ret = wait(NULL);
    if(ret == id)//判断等待的子进程
    {
        printf("wait success,ret:%d\n",ret);
    }
    sleep(5);
}

//...

之后重复上面的操作,运行之后可以看到子进程在五秒后进入僵尸状态,再五秒后被回收。从打印结果中可以看到,pid为ret == 13079 的子进程被成功回收。


多个进程

我们也可以通过循环来一次性创建多个子进程。比如如下书写main函数:

cpp 复制代码
int main()
{   
    int i = 0;
    for(i=0;i<N;i++)
    {
        pid_t id = fork();//创建子进程
        if(id == 0)
        {
            TestChild();
            exit(0);
        }
    }
}

由于父子进程的代码是共享的,子进程会进入if的判断并通过exit退出;而父进程则会继续进行循环并创建子进程,从而实现一次性创建多个子进程的功能。

下面我们在让子进程打印一下自己的pid信息,并通过sleep等待缓慢打印结果。

最后使用wait进行等待,将创建的僵尸子进程回收。完整代码如下:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define N 10//准备创建十个进程

void TestChild()
{
    int cnt = 5;
    while(cnt)
    {
        printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());
        cnt--;
        sleep(1);
    }
}

int main()
{   
    int i = 0;
    for(i=0;i<N;i++)
    {
        pid_t id = fork();//创建子进程
        if(id == 0)
        {
            TestChild();
            exit(0);
        }
        printf("create child process: %d success\n",id);
    }
    sleep(10);

    //等待
    for(i = 0;i < N;i++)
    {
        pid_t id = wait(NULL);
        if(id>0)
        {
            printf("wait %d success\n",id);
        }
    }
    sleep(5);

    return 0;
}

运行之后可以看到一瞬间创建出了十个僵尸子进程,在10秒后者十个子进程被全部回收,只剩下父进程仍在运行;再5秒后所有进程结束。


阻塞等待

在上面的代码中,如果子进程始终不退出:

cpp 复制代码
for(i=0;i<N;i++)
    {
        pid_t id = fork();//创建子进程
        if(id == 0)
        {
            TestChild();
            //进程始终不退出
            //exit(0);
        }
    }

并且我们中途也不进行等待sleep(10),直接让父进程进入等待,那么:

父进程默认在wait的时候调用这个系统调用的时候,也就不返回;该状态就为阻塞状态。所以一个进程在阻塞态的时候,不是一定在等待硬件资源,也可以等待软件资源,也就是这个子进程不退出,不进入僵尸状态的情况。

waitpid

下面我们来介绍waitpid。同样使用man命令查看一下:

我们可以发现waitpid的参数明显毕wait更多,不过二者都有一个 int *status 参数。这些参数表示什么呢?

参数:

  • pid: Pid=-1,等待任一个子进程。与wait等效
  • pid>0:等待其进程ID与pid相等的子进程。
  • status: WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
  • options: WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

在上面的代码中,int *status 参数部分可以设置为NULL;此时表示进程等待可以不关心进程的状态。

相关推荐
Lsir10110_16 分钟前
【Linux】中断 —— 操作系统的运行基石
linux·运维·嵌入式硬件
Sheffield22 分钟前
command和shell模块到底区别在哪?
linux·云计算·ansible
历程里程碑34 分钟前
Linux20 : IO
linux·c语言·开发语言·数据结构·c++·算法
郝学胜-神的一滴36 分钟前
深入浅出:使用Linux系统函数构建高性能TCP服务器
linux·服务器·开发语言·网络·c++·tcp/ip·程序人生
承渊政道40 分钟前
Linux系统学习【Linux系统的进度条实现、版本控制器git和调试器gdb介绍】
linux·开发语言·笔记·git·学习·gitee
技术路上的探险家41 分钟前
Ubuntu下Docker与NVIDIA Container Toolkit完整安装教程(含国内源适配)
linux·ubuntu·docker
代码AC不AC44 分钟前
【Linux】深入理解缓冲区
linux·缓冲区·标准错误
Doro再努力1 小时前
【Linux操作系统12】Git版本控制与GDB调试:从入门到实践
linux·运维·服务器·git·vim
全栈工程师修炼指南1 小时前
Nginx | stream content 阶段:UDP 协议四层反向代理浅析与实践
运维·网络·网络协议·nginx·udp
Lsir10110_1 小时前
【Linux】进程信号(上半)
linux·运维·服务器