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;此时表示进程等待可以不关心进程的状态。

相关推荐
AI_零食几秒前
开源鸿蒙跨平台Flutter开发:生日纪念日提醒应用
运维·flutter·开源·harmonyos·鸿蒙
ID_180079054731 分钟前
Python解析小红书(XHS)笔记评论 API,json数据返回参考
java·服务器·数据库
努力努力再努力wz3 分钟前
【C++高阶系列】告别内查找局限:基于磁盘 I/O 视角的 B 树深度剖析与 C++ 泛型实现!(附B树实现源码)
java·linux·开发语言·数据结构·c++·b树·算法
mhkxbq9 分钟前
昆仑G5580、G5680 V2、G2280及泰山鲲鹏200,AI大数据优选服务器
大数据·服务器·人工智能
艾莉丝努力练剑18 分钟前
【QT】Qt常用控件与布局管理深度解析:从原理到实践的架构思考
linux·运维·服务器·开发语言·网络·qt·架构
以太浮标19 分钟前
华为eNSP模拟器综合实验之- WLAN瘦AP配置实战案例详解
运维·网络·网络协议·华为·智能路由器·信息与通信
个性小王20 分钟前
华为-AC+FIT AP组网(web方式)
运维·网络·华为
码云数智-园园29 分钟前
Python 列表与元组:从核心区别到实战选型
运维·服务器·windows
zhangzeyuaaa29 分钟前
Logstash 单次运行模式下的数据丢失陷阱:Output 缓冲区丢弃的根因与根治方案
运维·logstash
格林威30 分钟前
Linux系统工业相机:Linux udev 规则绑定相机设备
linux·运维·开发语言·人工智能·数码相机·计算机视觉·工业相机