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

相关推荐
c++逐梦人2 小时前
进程的优先级与切换
linux·服务器·操作系统
重生之绝世牛码2 小时前
Linux软件安装 —— Redis集群安装(三主三从)
大数据·linux·运维·数据库·redis·数据库开发·软件安装
网安CILLE2 小时前
Wireshark 抓包实战演示
linux·网络·python·测试工具·web安全·网络安全·wireshark
是jin奥2 小时前
Ubuntu 18 安装 nodejs 合适版本
linux·ubuntu·vim
网硕互联的小客服2 小时前
如何彻底删除CentOS自带的postfix服务释放25端口?
linux·运维·centos
七七powerful2 小时前
docker 部署dirsearch并进行目录遍历扫描
运维·docker·容器
天码-行空2 小时前
CentOS 误删 /dev 目录救援方案
linux·运维·centos
小码吃趴菜2 小时前
mysql
linux·运维·服务器
呉師傅2 小时前
东芝3525AC彩色复印机打印配件寿命和打印错误记录方法【实际操作】
运维·服务器·网络·windows·电脑