精华内容
下载资源
问答
  • waitpid
    2021-01-17 13:19:40

    营销树今天精心准备的是《waitpid》,下面是详解!

    linux 中的waitpid函数的返回值问题

    TheprocessIDofthechildwhichexited,orzeroifWNOHANGwasusedandnochildwasavailable,or-1onerror(inwhichcaseerrnoissettoanappropriatevalue).者是man中的解释,我不明白在什么情况下...

    The process ID of the child which exited, or zero if WNOHANG was used

    and no child was available, or -1 on error (in which case errno is set

    to an appropriate value).

    者是man 中的解释,我不明白在什么情况下会返回0,为什么返回0,最好给举个例子,巨大的感谢了

    展开

    -1 是出现错误的返回值,我就不说了。

    0 只有当你的 waitpid 第三个参数包含 WNOHANG 的时候才有可能。

    比如

    pid = fork();

    if (pid == 0) {

    //child process

    while(1) {

    printf("aaa\n");

    sleep(1);

    }

    } else {

    // parent

    option = 0;

    result = waitpid(pid, NULL, option);

    。。。。。

    }

    上面的代码,由于子进程永远不会结束(除非它被kill),父进程会一直停在 waitpid 那个系统调用,等待子进程结束 (当子进程结束后, waitpid 的返回值等于子进程 pid)。

    但是如果 option=WNOHANG; 父进程不会停在 waitpid 那里, waitpid 会立刻返回 0 ,表示被等待的子进程并没有结束。

    linux里面的wait和waitpid是什么?

    wait和waitpid的不同在于wait会令调用者阻塞直至某个子进程终止而waitpid则可以通过设置一个选项来设置为非阻塞,另外waitpid并不是等待第一个结束的进程而是等待参数中pid指定的进程。

    waitpid提供了wait所没有的三个特性:

    1 waitpid使我们可以等待指定的进程

    2 waitpid提供了一个无阻塞的wait

    3 waitpid支持工作控制

    具体可以查看APUE page202

    求助,SIGCHLD和waitpid的关系

    其实这两者之间没有必然的关系。

    SIGCHILD只是在子进程退出的时候发送给父进程的一个信号值,这是一种异步通知父进程的方式.父进程可以捕获,忽略这个信号,忽略这个信号也是避免僵尸进程的一种方式.

    waitpid or wait回收子进程的结束状态,避免子进程进入僵尸状态.

    主进程可以直接调用waitpid or wait来回收子进程的结束状态,不一定非得通过SIGCHILD信号处理函数,也就是说waitpid or wait不是依靠SIGCHLD信号是否到达来判断子进程是否结束.但是如果主进程除了回收子进程状态以外还有其他的业务需要处理那么最好是通过SIGCHILD信号处理函数来调用waitpid or wait,因为这是异步的操作.

    如果注册了SIGCHLD信号处理函数,那么就需要等待SIGCHLD信号的到达并且完成信号处理函数,waitpid or wait才能接受到子进程的的退出状态.

    前面的3点可能很多同学都知道,但是对第4点可能没有关注过,其实以前我也没有关注过第4点,某天man system的时候看到"During execution of the command, SIGCHLD will be blocked",觉得有点不解."为什么要阻塞SIGCHLD信号呢?是不是跟waitpid有关呢?"

    在system实现中会调用waitpid来回收子进程的状态,首先想到的一点是:阻塞SIGCHLD是为了避免主进程已经注册的SIGCHLD处理函数回收所有的子进程状态,那么在system中的waitpid调用会导致ECHILD(No child processes)的错误.为了证实自己的想法是否正确在网上查了一下,最后发现还跟第4点有关系,因为如果不阻塞SIGCHLD信号并且主进程注册了SIGCHLD信号处理函数,那么就需要等主进程的信号处理函数返回waitpid才能接受到子进程的退出状态,也就是如果信号处理函数需要1min才能处理完那么system也需要1min才能返回.所以在调用system函数的时候阻塞SIGCHLD,这样在执行期间信号被阻塞就不会调用信号处理函数了,system中的waitpid就能"及时"的获取到子进程的状态,然后"及时"退出.

    wait和waitpid的区别

    wait    英[weɪt]    美[wet]

    vt.    等待; 等候; (尤指长期地) 希望; 盼望;

    vi.    准备妥; 在手边; 可得到; 可使用;

    vt.    推迟,搁置,延缓;

    [例句]I walk to a street corner and wait for the school bus

    我走到街角等校车。

    [其他]    第三人称单数:waits 现在分词:waiting 过去式:waited过去分词:waited

    waitpid 会暂时停止目前进程的执行,直到有信号来到或子进程结束。

    关于waitpid函数的问题,为什么最后子进程没退出来

    WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。 raise(SIGSTOP);只不过是让子进程暂停,并没有结束进程。所以返回值为0 还有ret=kill(result,SIGKILL)==0这句实际是这么执行的ret=(kill(r

    OS.WNOHANG 是什么意思?

    在交互式下执行os.WNOHANG结果是1....

    在交互式下执行 os.WNOHANG 结果是1.

    那是一个os模块的常量,一般在os.waitpid(-1 , os.WNOHANG)里面用

    -1 表示等待所有的子进程结束,作用相当于调用wait ,WNOHANG表示不使父进程挂起,而立即返回

    C语言问题 关于implicit declaration of function

    我在被调用函数所在文件如1.c的头文件1.h申明了这个函数,在调用函数的文件也包含了被调用文件的1.h但是为什么还会出现implicitdeclarationoffunction隐式申明函数的警告呢...

    我在被调用函数所在文件如1.c的头文件1.h申明了这个函数,在调用函数的文件也包含了被调用文件的1.h 但是为什么还会出现 implicit declaration of function 隐式申明函数的警告呢

    有两种情况会产生这种警告

    1 没有把函数所在的c文件生成.o目标文件

    2 在函数所在的c文件中定义了,但是没有在与之相关联的.h文件中声明

    在linux系统下实现对system() 函数对fork() ,exec(...

    不知道你这实现这些函数的调用是什么意思,是要重写这些接口吗?还是举个例子说明?

    我解释一下吧:

    (1)system()其实就是对fork()和exec()函数族等的封装。

    (2)fork()是用来产生子进程的,是现在我知道的唯一一个返回两个值的函数(有过有另外的,麻烦网友指出),返回-1表示执行失败;否则返回大于0的值时,表示是子进程的进程号,返回0时,表示父进程创建子进程成功。

    (3)exec()不是一个函数,是函数族,有execl(),execv(),execle(),execve(),execlp(),execvp(),它们常用于子进程中“脱胎换骨”,就是父进程创建子进程后,子进程几乎是父进程的拷贝(只有很少的东西不一样,如进程号(PID)等),然后子进程调用exec()函数族执行其他的程序,即将原来进程的东西全部清除掉,称为一个崭新的进程,所以叫“脱胎换骨”。

    (4)waitpid()是用在父进程中等待进程退出的,如果父进程不调用这个接口,那么它有可能先于子进程退出,那么子进程就会称为孤儿进程,继而被init进程(PID为1的进程,Linux启动后第一个启动的进程)收养。或者父进程并未退出,也未调用这个接口,但是子进程已经执行完成,那么子进程就会成为一个僵尸进程。

    具体例子在网上找找吧,都不是很难。

    进程间通信中wait函数有什么作用

    调用wait或waitpid有三种不同的情况发生:

    1、如果其所有子进程都还在运行,则阻塞

    2、如果一个子进程终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回

    3、如果它没有任何子进程,则立即出错返回

    如果进程由于接收到SIGCHLD信号而调用wait,则可期望wait会立即返回,但是如果在任意时刻调用wait,则进程可能会阻塞。

    在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞。

    waitpid并不等待在其调用之后的第一个终止子进程,他有若干选项,可以控制他所等待的进程。

    waitpid函数提供wait函数没有提供的三个功能:

    1、waitpid可等待一个特定的进程,而wait则返回任一终止子进程的状态

    2、waitpid提供了一个wait的非阻塞版本

    3、waitpid支持作业控制

    waitpid

    waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。

    更多相关内容
  • C语言wait()函数:结束(中断)进程函数(常用) 头文件: #include <sys> #include 定义函数: pid_t wait (int * status); 函数说明:wait()会暂时停止目前进程的... 子进程的结束状态值请参考waitpid(). 返回值:
  • 这个包带有两个程序: waitpid和waitall 。 第一个可用于等待指定的 PID,后者可用于等待具有指定名称的进程。 例子: $ waitpid -v 5323 5266 5323: waiting 5266: waiting 5323: received SIGINT 5323: exited...
  • 这个进程的父进程可以调用wait或者waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在shell中用特殊变量$?查看,因为shell是它的父进程,当它终止时,shell调用wait或者waitpid得到它...

    一个进程终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或者waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在shell中用特殊变量$?查看,因为shell是它的父进程,当它终止时,shell调用wait或者waitpid得到它的退出状态,同时彻底清除掉这个进程。

    1 wait回收子进程和异常终止信号

    wait函数

    // wait函数: 回收子进程退出资源, 阻塞回收任意一个。
    pid_t wait(int *status)
    // 参数:(传出) 回收进程的状态。
    // 返回值:成功: 回收子进程的pid
    // 失败: -1, errno
    // 函数作用1: 阻塞等待子进程退出【等子进程死亡,回收】
    // 函数作用2: 清理子进程残留在内核的 pcb 资源
    // 函数作用3: 通过传出参数,得到子进程结束状态

    下面这个例子,使用wait来阻塞回收子进程

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/wait.h>
    int main(int argc, char *argv[])
    {
        pid_t pid, wpid;
        int status;
        pid = fork();
        if (pid == 0)
        {
            printf("I'm child, my pid = %d, my parent pid = %d\n", getpid(), getppid());
            sleep(9);
            printf("I'm child, I'm going to die\n");
        }
        else if (pid > 0)
        {
            // 父进程回收子进程
            wpid = wait(&status);
            if (wpid == -1)
            {
                perror("wait error\n");
                exit(1);
            }
            printf("parent wait finish:%d\n", wpid);
        }
        else if (pid == -1)
        {
            perror("fork error\n");
            exit(1);
        }
        return 0;
    }

    执行

    改进

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/wait.h>
    int main(int argc, char *argv[])
    {
        pid_t pid, wpid;
        int status;
        pid = fork();
        if (pid == 0) {
            printf("I'm child,my pid = %d, my parent = %d\n", getpid(), getppid());
            sleep(10);
            printf("---------------I'm going to die-------------------\n");
            // 特殊值,演示正常退出的返回值
            return 73;
        }
        else if (pid > 0) {
            // 父进程回收子进程 如果子进程未终止,父进程会阻塞在这个函数上
            // wpid = wait(NULL); // 不关心子进程结束原因
            wpid = wait(&status);
            if (wpid == -1) {
                perror("wait error\n");
                exit(1);
            }
            // 为真
            if (WIFEXITED(status)) {
                // 说明子进程正常终止
                printf("child exit with %d\n", WEXITSTATUS(status));
            }
            // 为真,说明子进程被信号终止
            if (WIFSIGNALED(status)) {
                printf("child is killed signal with %d\n",WTERMSIG(status));
            }
            printf("parent wait finish:%d\n", wpid);
        } else if (pid == -1) {
            perror("fork error\n");
            exit(1);
        }
    
        return 0;
    }

    status的作用:

    获取子进程正常终止值:
    WIFEXITED(status) --》 为真 --》调用 WEXITSTATUS(status) --》 得到 子进程 退出值。
    
    获取导致子进程异常终止信号:
    WIFSIGNALED(status) --》 为真 --》调用 WTERMSIG(status) --》 得到 导致子进程异常终止的信号编号
    man 2 wait # 查看函数

    执行

    异常退出

    2 waitpid回收子进程

    waitpid函数

    // waitpid函数: 指定某一个进程进行回收。可以设置非阻塞。
    waitpid(-1, &status, 0) == wait(&status);
    pid_t waitpid(pid_t pid, int *status, int options)
    // 参数:
    // pid:指定回收某一个子进程pid
    // > 0: 待回收的子进程pid
    // -1:任意子进程
    // 0:同组的子进程。
    // status:(传出) 回收进程的状态。
    // options:WNOHANG 指定回收方式为,非阻塞。
    // 返回值:
    // > 0 : 表成功回收的子进程 pid
    // 0 : 函数调用时, 参3 指定了WNOHANG。并且,没有子进程结束。
    // -1: 失败。errno

    一次wait/waitpid函数调用,只能回收一个子进程。上一个例子,父进程产生了5个子进程,wait会随机回收一个,捡到哪个算哪个。

    父进程先回收,子进程后结束

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/wait.h>
    
    int main(int argc, char *argv[])
    {
        int i;
        pid_t pid, wpid;
        for (i = 0; i < 5; i++)
        {
            pid = fork();
            if (pid == 0)
            {
                break;
            }
        }
        if(i == 5)
        {
            // sleep(5);
            // wait(NULL); // 一次wait和waitpid调用,只能回收一个子进程,无差别回收
            wpid = waitpid(-1, NULL, WNOHANG);
            if (wpid == -1)
            {
                perror("waitpid error\n");
                exit(1);
            }
            printf("I'm parent, with a child finish:%d\n", wpid);
        }
        else
        {
            sleep(i);
            printf("I'm %dth child\n", i + 1);
        }
        
        return 0;
    }

    执行

    父进程休眠,等待子进程结束,回收,即放开注释的sleep(5),打印pid

    指定某一个进程回收。

    (1)阻塞回收
     

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/wait.h>
    
    int main(int argc, char *argv[])
    {
        int i;
        pid_t pid, wpid, tpid;
        for (i = 0; i < 5; i++)
        {
            pid = fork();
            if (pid == 0)
            { // 循环期间,子进程不fork
                break;
            }
            if(i == 2)
            {               // 父进程执行
                tpid = pid; // 保存子进程pid
                printf("----------pid = %d\n", tpid);
            }
        }
        if(i == 5)
        {
            // sleep(5);
            // wait(NULL); // 一次wait和waitpid调用,只能回收一个子进程,无差别回收
            printf("-----parent, before waitpid pid = %d\n", tpid);
            wpid = waitpid(tpid, NULL, 0); // 指定一个进程回收, 阻塞回收
            if (wpid == -1)
            {
                perror("waitpid error\n");
                exit(1);
            }
            printf("I'm parent, with a child finish:%d\n", wpid);
        }
        else
        {
            sleep(i);
            printf("I'm %dth child, pid = %d\n", i + 1, getpid());
        }
    
        return 0;
    }

    执行

    这里会和中断提示符混在一起,并且不会结束。

    (2)非阻塞+时延:这样终端提示符就不会混在输出里。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/wait.h>
    
    int main(int argc, char *argv[])
    {
        int i;
        pid_t pid, wpid, tpid;
        for (i = 0; i < 5; i++)
        {
            pid = fork();
            if (pid == 0)
            { // 循环期间,子进程不fork
                break;
            }
            if(i == 2)
            {               // 父进程执行
                tpid = pid; // 保存子进程pid
                printf("----------pid = %d\n", tpid);
            }
        }
        if(i == 5)
        {
            sleep(5);
            // wait(NULL); // 一次wait和waitpid调用,只能回收一个子进程,无差别回收
            printf("-----parent, before waitpid pid = %d\n", tpid);
            wpid = waitpid(tpid, NULL, WNOHANG); // 指定一个进程回收, 不阻塞 
            if (wpid == -1)
            {
                perror("waitpid error\n");
                exit(1);
            }
            printf("I'm parent, with a child finish:%d\n", wpid);
        }
        else
        {
            sleep(i);
            printf("I'm %dth child, pid = %d\n", i + 1, getpid());
        }
    
        return 0;
    }

    执行

    3 waitpid回收多个子进程

    wait、waitpid 一次调用,回收一个子进程。
    想回收多个。while

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/wait.h>
    
    int main(int argc, char *argv[])
    {
        int i;
        pid_t pid, wpid;
        for (i = 0; i < 5; i++)
        {
            pid = fork();
            if (pid == 0)
            { // 循环期间,子进程不fork
                break;
            }
        }
        if(i == 5)
        {
            /* // 使用阻塞方回收子进程
            while((wpid = waitpid(-1, NULL, 0))) {
                printf("wait child %d\n",wpid);
            }*/
            // 非阻塞
            while ((wpid = waitpid(-1, NULL, WNOHANG)) != -1)
            {
                if (wpid > 0)
                {
                    printf("wait child %d\n", wpid);
                }
                else if (wpid == 0)
                {
                    sleep(1);
                    continue;
                }
            }
        }
        else
        {
            sleep(i);
            printf("I'm %dth child, pid = %d\n", i + 1, getpid());
        }
    
        return 0;
    }

    执行

    全部回收了。 

    想暴力回收子进程

    kill -9 父进程号
    展开全文
  • Linux系统编程之waitpid函数详解

    千次阅读 2022-03-08 20:13:27
    5.1 为什么要进行进程资源的回收 当一个子进程退出之后,...这个子进程内核区的PCB资源必须得由它的父进程,调用wait 或者 waitpid函数完成对其子进程的回收,从而避免了对系统资源的浪费! 5.2 孤儿进程 ...

    5.1 为什么要进行进程资源的回收

            当一个子进程退出之后,该进程能够回收自己的用户区的资源,但是不能回收内核空间区的PCB(process control block 进程控制块)资源。

    (即:子进程自己本身是无法完成对内核的PCB资源的回收,此时就会非常浪费linux系统的资源了!)

            这个子进程内核区的PCB资源必须得由它的父进程,调用wait 或者 waitpid函数完成对其子进程的回收从而避免了对系统资源的浪费!

    5.2 孤儿进程

    (本身不是错误!因为孤儿进程还是可以存活着的,可以给init进程回收其内核区的PCB资源的,一切都还是正常的/可接受的case)

            孤儿进程的概念:

            若子进程的父进程已经死掉(先于子进程执行退出),而子进程还活着,那么这个子进程此时就会变成孤儿进程了!

            (在Linux操作系统下,是必须要保证每一个进程都有一个父进程的)为了保证每个进程都有一个父进程,孤儿进程会被一个进程id PID==1的叫做init的进程回收掉(领养),那么init进程就成为了该孤儿进程的养父进程了。当孤儿进程执行退出之后,由init进程完成对孤儿进程资源的回收。

            模拟孤儿进程的案例: 

            编写模拟孤儿进程的代码,并验证孤儿进程的父进程是否由原来的父进程变为init进程。

    test codes1:

    //测试代码,测试父进程先死掉(先执行退出)后,是否由于PID==1的init进程把该子进程领养了!
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<string.h>
    #include<stdlib.h>
    int main()
    {
    	printf("before fork: pid==[%d]\n",getpid());
    	//创建子进程函数原型:
    	//pid_t fork(void);
    	pid_t pid = fork();
    	if(pid < 0)//fork失败的case
    	{
    		perror("fork error!");
    		return -1;
    	}
    	else if(pid == 0)//pid == 0 时,则当前进程为子进程
    	{
    		printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    	}
    	else//pid > 0 时,则当前进程为父进程
    	{ 
    		//pid_t getpid(void);
    		//这个函数会返回调用该函数的进程的ID
    		//(哪个进程调用它,它就返回谁的PID)
    		printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    		sleep(2);//让父进程后退出,此时就不会出现孤儿子进程的case了!
    	}
    	return 0;
    }

    result1:

      

    此时属于正常case,不会出现僵尸子进程的case! (因为其父进程sleep了2s,等子进程先结束)

    补充:

            pid_t getpid(void);

            这个函数会返回调用该函数的进程的ID

            (哪个进程调用它,它就返回谁的PID)

            pid_t getppid(void);

            这个函数会返回调用该函数的进程的父进程的ID

            (哪个子进程调用它,它就返回它的父进程的PID)

     test codes2:

    //测试代码,测试父进程先死掉(先执行退出)后,是否由于PID==1的init进程把该子进程领养了!
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<string.h>
    #include<stdlib.h>
    int main()
    {
    	//创建子进程函数原型:
    	//pid_t fork(void);
    	pid_t pid = fork();
    	if(pid < 0)//fork失败的case
    	{
    		perror("fork error!");
    		return -1;
    	}
    	else if(pid == 0)//pid == 0 时,则当前进程为子进程
    	{
    		printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    	}
    	else//pid > 0 时,则当前进程为父进程
    	{ 
    		//pid_t getpid(void);
    		//这个函数会返回调用该函数的进程的ID
    		//(哪个进程调用它,它就返回谁的PID)
    		printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    	}
    	return 0;
    }

    result2:

    小总结:

            孤儿进程:当父进程先退出,子进程后退出时,子进程就变成孤儿进程了,此时由init进程(PID==1)回收。当子进程也退出之后(他自己会回收自己用户区的资源),而init进程则会回收养子进程的内核区PCB资源。

    5.3 僵尸进程(在开发项目时,一定要注意僵尸进程的问题!不要给我写出)

    (本身就是个错误!因为僵尸进程是已经死掉了的,如果不杀死其原父进程---给init进程回收其内核区的PCB资源的话,此时系统就一直无法回收其内核区PCB资源,这样迟早你的系统资源给消耗掉,消耗完了你的系统就无法再创建新的进程了。一旦系统无法创建新的进程,这将会是很可怕的事情!此时就是不正常的/不可接受的case)

            僵尸进程的概念:

            若子进程死了,父进程还活着,但是父进程没有调用wait 或者 waitpid函数完成对子进程的回收时(或者说父进程调用waitpid函数对其子进程进行回收时,子进程还活着,且waitpid函数的options==WNOHANG非阻塞的),此时的子进程就变成僵尸进程

    (当当子进程先退出,而父进程没有完成对子进程资源的回收,此时的子进程就会变成僵尸进程!)

            补充:在ps ajx命令下,你可以看到所有关于进程的信息,此时,假设某父进程名字为zombie,若你还看到有[zombie]<defunct>这样的进程,就说明此时你的系统存在着僵尸进程!<defunct>这个关键字就标识了这个进程为僵尸进程!因为defunct是adj,消失的,不存在的意思!

            如何解决 僵尸进程(不回收导致的系统资源浪费)的问题呢

            答:

            临时的非最佳的解决方法是:要想把僵尸进程的资源马上给回收掉,需要你通过使用kill -9 pid命令,直接杀掉其对应的父进程(因为僵尸子进程已经是个死掉了的进程,它没法接受并回应你给它传的kill -9 僵尸子进程pid这样的杀死自己的命令)。

            原因:杀死僵尸子进程的原父进程,可以让该僵尸子进程给init进程领养,由init进程来do回收它的资源的工作。这就可以避免僵尸子进程的资源没有被回收而导致的系统资源的浪费!

            最佳的最好的解决方法是:在子进程先于父进程退出后,让父进程调用wait或者waitpid函数对其资源进行回收!(这个后面学到wait以及waitpid函数时就会讲到!下面先对临时的解决方法写一个代码do验证测试!)

            模拟僵尸进程的案例:

            编写模拟僵尸进程的代码,验证若子进程先于父进程退出,而父进程又没有调用wait或者waitpid函数对其进程内核区PCB资源的回收,从而是的子进程变成了僵尸进程。

    test codes:

    //测试代码,测试子进程先死掉(先执行退出)后,父进程没有回收子进程的资源
    //此时的子进程就会变成僵尸进程了!
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<string.h>
    #include<stdlib.h>
    int main()
    {
    	printf("before fork: pid==[%d]\n",getpid());
    	//创建子进程函数原型:
    	//pid_t fork(void);
    	pid_t pid = fork();//fork开一个子进程!
    	if(pid < 0)//fork失败的case
    	{
    		perror("fork error!");
    		return -1;
    	}
    	else if(pid == 0)//pid == 0 时,则当前进程为子进程
    	{
    		printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    	}
    	else//pid > 0 时,则当前进程为父进程
    	{ 
    		sleep(200);//父进程休眠100s
    		//让父进程后退出,此时就不会出现孤儿子进程的case!
    		//但是,此时就会出现子进程资源没有被父进程回收的case!
    		//因此,此时的子进程就会变成僵尸进程!
    		printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    	}
    	return 0;
    }

    result:

    输入命令ps ajx后:

    :这个[zombie]<defunct>就代表了僵尸子进程!因为子进程的用户区(包含名称等的信息)是由父进程copy过去的,所有子进程的名字和父进程的名字是一样的!(这个小点是你必须要care的 !必须要注意的!)

    输入命令kill -9 1835252(尝试kill掉僵尸子进程)且 输入命令ps ajx后:

    这说明,僵尸进程已经死掉了,你没法用活的kill -9命令去删除它!

    那么我们如何让该僵尸进程给马上回收呢?

    答:直接杀死该僵尸子进程的父进程,然后该僵尸进程马上就给init进程领养了,领养之后由init进程把这个僵尸进程的资源回收掉!

    kill 僵尸进程的父进程 且 输入命令ps ajx后:

            解决上述僵尸进程问题的最佳方案:在子进程先于父进程退出后,马上让父进程调用wait或者waitpid函数对其进行资源的回收工作!

            因此,下面我们将学习wait以及waitpid这两个进程回收函数!

    5.4 进程回收函数

    不论是wait/waitpid函数,都是在父进程中调用该函数以完成对其子进程资源的回收工作的!

           ①wait函数(是在父进程中调用该函数以完成对其子进程资源的回收工作)

                    函数原型:

                    pid_t wait(int* wstatus);

                    //若不关心进程的结束状态(结束原因)时,可以传NULL进去!

                    函数作用:

                            1-- 阻塞并等待子进程退出

                            (注意:这是个阻塞函数!一直阻塞,等到子进程退出后该函数才能返回!)

                            2-- (由父进程)回收子进程残留资源

                            3-- 获取子进程结束状态(退出原因)

                    函数返回值:

                            if成功:清理掉子进程ID;

                            if失败:返回-1(表示子进程全都回收掉了,没得子进程回收了)

                    wstatus 参数(可以通过man 2 wait命令查询):子进程的退出状态 -- 传出参数

    (只要你不传NULL的wstatus进去,后续需要使用wstatus参数来写代码了解子进程退出的状态的,否则,你根本就无需使用wstatus参数的写代码了)

            (这些宏参数,不需要你记住!你只需要会查阅,找到使用方法即可!)

                            WIFEXITED(wstatus):为非0 -> 进程正常结束

                            WEXITSTATUS(wstatus):获取进程退出状态

                            WIFSIGNALED(wstatus):为非0 -> 进程异常终止(此时进程时被信号signal杀死的,比如kill -9 pid信号)

                            WTERMSIG(wstatus):取得 使进程终止的信号的编号

    wait 函数练习案例:

    test codes1:

    //wait函数测试代码,测试让父进程调用wait函数回收子进程资源!
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>//这个头文件可以 在 man 2 wait中看到!
    #include<string.h>
    #include<stdlib.h>
    int main()
    {
    	printf("before fork: pid==[%d]\n",getpid());
    	int i = 0;
    	//循环创建3个子进程
    	for(i = 0;i<3;++i){
    		pid_t pid = fork();//创建子进程
    		if(pid < 0){
    			perror("fork error!");
    			return -1;
    		}
    		else if(pid == 0){// 子进程
    			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			sleep(2);//让子进程休眠2s,以确认其父进程中的wait函数确实是阻塞函数!
    			break;//这个break就保证了所创建的进程均为兄弟进程!
    		}
    		else //pid > 0 父进程
    		{
    			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			pid_t exit_pid = wait(NULL);//wait函数会阻塞等待子进程退出后,将其回收!
    			if(exit_pid == -1){
    				printf("There is no child process already!");
    				break;
    			}
    			printf("wait函数成功返回!\nexit_pid==child_pid==[%d]\n",exit_pid);
    		}
    		printf("\n");
    	}
    	return 0;
    }

    result1:

     test codes2:

    //wait函数测试代码,测试让父进程调用wait函数回收子进程资源!
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<string.h>
    #include<stdlib.h>
    int main()
    {
    	printf("before fork: pid==[%d]\n",getpid());
    	int i = 0;
    	//循环创建3个子进程
    	for(i = 0;i<2;++i){
    		pid_t pid = fork();//创建子进程
    		if(pid < 0){
    			perror("fork error!");
    			return -1;
    		}
    		else if(pid == 0){// 子进程
    			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			sleep(2);
    			break;//这个break就保证了所创建的进程均为兄弟进程!
    		}
    		else //pid > 0 父进程
    		{
    			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			int wstatus = -1;
    			pid_t exit_wait_pid = wait(&wstatus);//wait函数会阻塞等待子进程退出后,将其回收!
    			if(exit_wait_pid == -1){
    				printf("There is no child process already!");
    				break;
    			}
    			if(WIFEXITED(wstatus)){//if WIFEXITED(wstatus) == true 子进程正常退出
    				printf("子进程 正常退出!\n");
    				//WEXITSTATUS(wstatus));返回子进程退出的状态(原因)
    				printf("子进程退出的状态(原因):%d\n",WEXITSTATUS(wstatus));
    			}
    			else if(WIFSIGNALED(wstatus)){//if WIFSIGNALED(wstatus) == true 子进程是被信号杀死而退出的!
    				int sigNumber = WTERMSIG(wstatus);//WTERMSIG(wstatus);返回杀死子进程的信号number
    				printf("子进程 由信号 signal[%d] 杀死了!\n",sigNumber);
    			}
    			printf("wait函数成功返回!\nexit_wait_pid==child_pid==[%d]\n",exit_wait_pid);
    		}
    		printf("\n");
    	}
    	return 0;
    }

     result2:

    test codes3:

    //wait函数测试代码,测试让父进程调用wait函数回收子进程资源!
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<string.h>
    #include<stdlib.h>
    int main()
    {
    	printf("before fork: pid==[%d]\n",getpid());
    	int i = 0;
    	//循环创建3个子进程
    	for(i = 0;i<2;++i){
    		pid_t pid = fork();//创建子进程
    		if(pid < 0){
    			perror("fork error!");
    			return -1;
    		}
    		else if(pid == 0){// 子进程
    			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			sleep(200);
    			break;//这个break就保证了所创建的进程均为兄弟进程!
    		}
    		else //pid > 0 父进程
    		{
    			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			int wstatus = -1;
    			pid_t exit_wait_pid = wait(&wstatus);//wait函数会阻塞等待子进程退出后,将其回收!
    			if(exit_wait_pid == -1){
    				printf("There is no child process already!");
    				break;
    			}
    			if(WIFEXITED(wstatus)){//if WIFEXITED(wstatus) == true 子进程正常退出
    				printf("子进程 正常退出!\n");
    				//WEXITSTATUS(wstatus));返回子进程退出的状态(原因)
    				printf("子进程退出的状态(原因):%d\n",WEXITSTATUS(wstatus));
    			}
    			else if(WIFSIGNALED(wstatus)){//if WIFSIGNALED(wstatus) == true 子进程是被信号杀死而退出的!
    				int sigNumber = WTERMSIG(wstatus);//WTERMSIG(wstatus);返回杀死子进程的信号number
    				printf("子进程 由信号 signal[%d] 杀死了!\n",sigNumber);
    			}
    			printf("wait函数成功返回!\nexit_wait_pid==child_pid==[%d]\n",exit_wait_pid);
    		}
    		printf("\n");
    	}
    	return 0;
    }

    result3:(演示用kill -9 pid 命令杀死子进程的wait函数的返回状态的效果)

     

     wait函数小总结:

            pid_t wait(int* wstatus);

            注:若对子进程的退出状态不感兴趣的话,可以直接传NULL进去即可!

            参数:

            wstatus:子进程的退出状态

    if(WIFEXITED(wstatus))
    {
        WEXITSTATUS(wstatus)
    }
    else if(WIFSIGNALED(wstatus))
    {
        WTERMSIG(wstatus)
    }

            ②waitpid函数(比wait函数更高级,功能也更加强大,也是在父进程中调用该函数以完成对其子进程资源的回收工作)

                    函数原型:

                    pid_t waitpid(pid_t pid,int* wstatus,int options);

                    注:这个waitpid函数默认case下就是阻塞的函数!

                    函数作用:(同wait函数)

                            1-- 阻塞并等待子进程退出

                            (注意:这也是个阻塞函数!一直阻塞,等到子进程退出后该函数才能返回!)

                            2-- (由父进程)回收子进程残留资源

                            3-- 获取子进程结束状态(退出原因)

                    函数参数:

                    pid参数:

                            1-- 当pid == -1时 等待任一子进程。与wait等效(多用)    

                            (人话:就是回收所有(多个)的子进程)

    (当然,父进程每调用一次的waitpid函数就只能够回收一个子进程而已,一次该函数的调用并不能回收多个子进程!)

                            2-- 当pid > 0时 等待其进程ID 与 pid 相等的子进程(多用)    

                            (人话:就是回收指定(一个)PID的子进程)

                            3-- 当pid == 0时 等待进程组ID 与 目前进程相同的任何子进程。(少用/基本不用)     

                            (人话:回收任何和调用waitpid()函数的父进程在同一个进程组里面的所有程。

                            (进程都是有所属组的)

                            4-- 当pid < -1时 等待其组ID 等于 pid的绝对值得任一子进程。(少用/基本不用)   

                            (人话:回收在其他组的子进程

                    wstatus 参数(可以通过man 2 wait命令查询):子进程的退出状态 -- 传出参数 用法同wait函数。

    (只要你不传NULL的wstatus进去,后续需要使用wstatus参数来写代码了解子进程退出的状态的,否则,你根本就无需使用wstatus参数的写代码了)

            (这些宏参数,不需要你记住!你只需要会查阅,找到使用方法即可!)

                           WIFEXITED(wstatus):为非0 -> 进程正常结束

                            WEXITSTATUS(wstatus):获取进程退出状态

                            WIFSIGNALED(wstatus):为非0 -> 进程异常终止(此时进程时被信号signal杀死的,比如kill -9 pid信号)

                            WTERMSIG(wstatus):取得 使进程终止的信号的编号

                    options参数:

                            1-- 设置为options  == 0,表示waitpid函数为阻塞的。(该函数会阻塞卡在这儿,若有子进程,就回收;若没有子进程,一直卡着)

                            2-- 设置为options == WNOHANG,表示waitpid函数为非阻塞的(也即不管有没有子进程了,该函数都不会阻塞卡在这儿)

                            (通常,当你想把waitpid函数设置为非阻塞的时候(即让该函数不影响别的程序的执行,别只是卡在这儿),就让options == WNOHANG)

                    函数返回值:

                     >0:表示返回的是回收掉的子进程ID(PID);

                      -1:表示无子进程了;(表示子进程全都回收掉了,没得子进程回收了)

                     =0 且 第3个参数options为WNOHANG时:表示子进程正在运行(子进程尚未退出);

    在linux的终端下输入man 2 waitpid/wait命令可查询到:

                    waitpid函数案例练习:

    test codes1:

    //waitpid函数测试代码,测试让父进程调用waitpid函数回收子进程资源!
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<string.h>
    #include<stdlib.h>
    int main()
    {
    	printf("before fork: pid==[%d]\n",getpid());
    	int i = 0;
    	for(;i<3;++i){
    		//循环创建3个子进程
    		pid_t pid = fork();
    		if(pid < 0){
    			perror("fork error!");
    			return -1;
    		}
    		else if(pid==0){//当前进程为 子进程
    			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			sleep(2);
    			break;//这个break语句会保证所创建的子进程都为兄弟子进程
    		}
    		else{//pid > 0 当前进程为 父进程
    			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			//在父进程中调用waitpid函数!
    			pid_t inputPid = -1;//inputPid==-1表示我要回收当前所有的子进程
                //即等待着任一子进程do回收的意思
    			int* wstatus = NULL;//status == NULL表示我不关心子进程的退出状态
    			int options = 0;//options==0表示该函数是阻塞的
    			pid_t exit_waitpid_pid = waitpid(inputPid,wstatus,options);
    			if(exit_waitpid_pid > 0){//表示成功回收掉子进程了!
    				printf("成功回收掉子进程了,此时所回收掉的子进程id==[%d]\n",exit_waitpid_pid);
    			}
    			else if(exit_waitpid_pid == -1){
    				printf("当前没有需要回收掉的子进程了!\n");
    			}
    			else if(exit_waitpid_pid == 0 && options == WNOHANG){
    				printf("当前需要回收的子进程还在运行中,尚未退出!\n");
    			}
    		}
    		printf("\n");
    	}
    	return 0;
    }

    result1:

    test codes2:

    //waitpid函数测试代码,测试让父进程调用waitpid函数回收子进程资源!
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<string.h>
    #include<stdlib.h>
    int main()
    {
    	printf("before fork: pid==[%d]\n",getpid());
    	int i = 0;
    	for(;i<3;++i){
    		//循环创建3个子进程
    		pid_t pid = fork();
    		if(pid < 0){
    			perror("fork error!");
    			return -1;
    		}
    		else if(pid==0){//当前进程为 子进程
    			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			sleep(2);
    			break;//这个break语句会保证所创建的子进程都为兄弟子进程
    		}
    		else{//pid > 0 当前进程为 父进程
    			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			//在父进程中调用waitpid函数!
    			pid_t inputPid = -1;//inputPid==-1表示我要回收当前所有的子进程
    			int wstatus = -1;//&status != NULL表示我关心子进程的退出状态
    								//后续需要结合wstatus的参数写代码了解子进程的退出状态
    			int options = WNOHANG;//options==WNOHANG表示该函数是不阻塞的
    			pid_t exit_waitpid_pid = waitpid(pid,&wstatus,options);
    			if(exit_waitpid_pid > 0){//表示成功回收掉子进程了!
    				printf("成功回收掉子进程了,此时所回收掉的子进程id==[%d]\n",exit_waitpid_pid);
    				//成功回收掉子进程,才能够判断其退出状态!
    				if(WIFEXITED(wstatus)){
    					printf("子进程正常退出了!子进程的退出状态是:%d\n",WEXITSTATUS(wstatus));
    				}else if(WIFSIGNALED(wstatus)){
    					printf("子进程时由信号[%d]杀死的!\n",WTERMSIG(wstatus));
    				}
    			}
    			else if(exit_waitpid_pid == -1){
    				printf("当前没有需要回收掉子进程了,即:活着的子进程都已经死掉了退出了!\n");
    			}
    			else if(exit_waitpid_pid == 0 && options == WNOHANG){
    				printf("当前需要回收的子进程还在运行中,尚未退出!\n");
    			}
    			sleep(100);//让子进程变成僵尸进程!
    		}
    		printf("\n");
    	}
    	return 0;
    }

            解释:因为此刻,父进程调用waitpid函数对其子进程进行回收时,子进程还活着,且waitpid函数的options==WNOHANG非阻塞的),这样父进程又休息了100s,子进程才休息2s,因此子进程会先退出,此时父进程又没回收它的资源。因此,此时的子进程就变成僵尸进程了。 

    result2:(这个test2代码会产生3个僵尸进程,这是所用waitpid的一个弊端,你可以用临时回收子进程的方法:直接杀掉其对应的原父进程!让init进程领养它,并对其资源进行回收,这样才能避免浪费系统资源!)

     

    但是,用临时回收子进程的方法还是不太好的,因此我们对test2这种,父进程调用非阻塞的waitpid函数对其子进程回收时所遇到的问题的代码,进行改进,得到一份robust的代码!

    技巧:一直循环回收子进程的代码!知道没有子进程了/子进程都给回收掉了!

    so,下面这份代码是父进程调用非阻塞式的waitpid函数对其子进程do回收工作的标准代码范本

    test codes3:

    //waitpid函数测试代码,测试让父进程调用waitpid函数回收子进程资源!
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<string.h>
    #include<stdlib.h>
    int main()
    {
    	printf("before fork: pid==[%d]\n",getpid());
    	int i = 0;
    	for(;i<3;++i){
    		//循环创建3个子进程
    		pid_t pid = fork();
    		if(pid < 0){
    			perror("fork error!");
    			return -1;
    		}
    		else if(pid==0){//当前进程为 子进程
    			printf("\n");
    			printf("第[%d]个子进程被父进程do循环回收工作:",i+1);
    			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			sleep(1);
    			break;//这个break语句会保证所创建的子进程都为兄弟子进程
    		}
    		else{//pid > 0 当前进程为 父进程
    			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());
    			//在父进程中调用waitpid函数!
    			while(1){
    				//这个while循环,就已经可以保证 父进程一定是晚于子进程退出的了!
    				pid_t inputPid = -1;//inputPid==-1表示我要回收当前所有的子进程
    				int wstatus = -1;//&status != NULL表示我关心子进程的退出状态
    									//后续需要结合wstatus的参数写代码了解子进程的退出状态
    				int options = WNOHANG;//options==WNOHANG表示该函数是不阻塞的
    				pid_t exit_waitpid_pid = waitpid(pid,&wstatus,options);
    				if(exit_waitpid_pid > 0){//表示成功回收掉子进程了!
    					printf("成功回收掉子进程了,此时所回收掉的子进程id==[%d]\n",exit_waitpid_pid);
    					//成功回收掉子进程,才能够判断其退出状态!
    					if(WIFEXITED(wstatus)){
    						printf("子进程正常退出了!子进程的退出状态是:%d\n",WEXITSTATUS(wstatus));
    					}else if(WIFSIGNALED(wstatus)){
    						printf("子进程时由信号[%d]杀死的!\n",WTERMSIG(wstatus));
    					}
    				}
    				else if(exit_waitpid_pid == 0 && options == WNOHANG){
    					// printf("当前需要回收的子进程还在运行中,尚未退出!\n");
    					//因为这句子进程尚在运行的话肯定是大量打印的!
                        //so这些测试代码可以在测试程序时使用,但是在实际项目中就注释掉了吧!
    				}
    				else if(exit_waitpid_pid == -1){
    					printf("当前没有需要回收掉子进程了,即:活着的子进程都已经死掉了退出了!\n");
    					printf("此时,没有需要回收掉子进程了,就直接break 退出循环,无需再do回收子进程资源的工作了!\n");
    					break;//此时,没有需要回收掉子进程了,就直接break 退出循环,无需再do回收子进程资源的工作了!
    				}
    			}
    		}
    		printf("\n");
    	}
    	return 0;
    }

    result3:

    waitpid函数小总结:

    waitpid函数:
    pid_t waitpid(pid_t pid,int* status,int options);
        参数:
            pid:
                pid > 0:表示等待指定的子进程
                pid==-1:表示等待任一子进程
            status:
            同wait函数
            options:
                0:表示阻塞
                WNOHANG:表示不阻塞(不论有没有子进程退出,waitpid函数都会马上退出!不会阻塞在这儿!)
        返回值:
            > 0:是回收掉子进程的PID
            = 0:若options取值为WNOHANG,则表示子进程还活着
            -1:表示目前已经没有子进程了,此时那可以break掉循环回收子进程的while循环了!
    
        注意:调用一次waitpid/wait函数只能回收一个子进程而已!
    
    灵魂拷问:为什么一定是由于父进程来调用wait/waitpid函数来对其子进程的资源进行回收呢?
    答:因为子进程无法对自己的内核区PCB的资源进行回收!这个进程控制块的资源必须由其父进程去回收!
    一旦子进程先于父进程先退出,则此时的子进程就会变成僵尸进程!那么就会浪费系统资源!

    小练习:

    展开全文
  • linux中wait与waitpid

    千次阅读 2022-04-21 18:24:41
    因此就有了wait与waitpid wait(int *status) 用于父进程等待子进程,当子进程结束的时候,父进程才会被使用,相当于父进程被阻塞了;,同时其status用于父进程查询子进程当前是什么状态 wait获取staus后检测处理 宏...

    进程

    在linux中利用fork创建子进程,当子进程结束的时候,会产生僵尸进程,该僵尸进程会有保留一些内核的数据结构,供父进程查看子进程的状态;因此就有了wait与waitpid

    wait(int *status)

    用于父进程等待子进程,当子进程结束的时候,父进程才会被使用,相当于父进程被阻塞了;,同时其status用于父进程查询子进程当前是什么状态

    wait获取staus后检测处理
    宏定义  描述
    WIFEXITED(status) 如果进程子进程正常结束,返回一个非零值
        WEXITSTATUS(status) 如果WIFEXITED非零,返回子进程退出码
    WIFSIGNALED(status) 子进程因为捕获信号而终止,返回非零值
        WTERMSIG(status) 如果WIFSIGNALED非零,返回信号代码
    WIFSTOPPED(status) 如果进程被暂停,返回一个非零值
        WSTOPSIG(status) 如果WIFSTOPPED非零,返回信号代码
    

    waitpid(pid_t pid,int *status,int options)

    用于等待指定的子进程,当子进程结束的时候,父进程才会被调用

    • pid > 0; 不用管其他子进程,只要该pid还没有结束,父进程就不会结束;
    • pid = -1; 等待任何一个子进程,此时waitpid和wait的作用一模一样
    • 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
    • pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
      options允许改变waitpid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起
      返回值:成功返回等待子进程的pid,失败返回-1

    wait使用

    #include <iostream>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    using namespace std;
    int main()
    {
        int i = 0; 
        cout<<"before fork"<<endl;
        pid_t pid = fork();
        cout<<"after fork"<<endl;
        if(pid < 0)
        {
            cout<<"error"<<endl;
            return 1;
        }
        else if(pid == 0)
        {
            cout<<"fork sucess, this is son process"<<endl;
            while(i < 10)
            {
                i += 1;
                cout<<"this is son process, i = "<<i<<endl;
                sleep(1);
            }
        }
        else
        {
            int flag;
            pid_t p = wait(&flag); // 当子进程结束的时候,父进程才会调用,或者有信号来的时候父进程才会调用
            cout<<"fork sucess, this is father process, son process pid="<<pid<<endl;
            while(i < 10)
            {
                i += 2;
                cout<<"this is father process i="<<i<<endl;
                sleep(2);
            }
            i = WEXITSTATUS(flag); // 判断是否存在
            cout<<"i:"<<i<<"flag:"<<flag<<"p:"<<p<<endl;
        }
        cout<<"process destroy"<<endl;
        return 0;
    }
    

    运行结果, 等待父进程结束才运行

    before fork
    after fork
    after fork
    fork sucess, this is son process
    this is son process, i = 1
    this is son process, i = 2
    this is son process, i = 3
    this is son process, i = 4
    this is son process, i = 5
    this is son process, i = 6
    this is son process, i = 7
    this is son process, i = 8
    this is son process, i = 9
    this is son process, i = 10
    process destroy
    fork sucess, this is father process, son process pid=5170
    this is father process i=2
    this is father process i=4
    this is father process i=6
    this is father process i=8
    this is father process i=10
    i:0flag:0p:5170
    process destroy
    

    waitpid

    先举个例子,当创建多个进程的时候,多个子进程与主进程同时运行

    #include <iostream>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <vector>
    using namespace std;
    pid_t create_fork()
    {
        int i = 0;
        pid_t pid = fork();
        if(pid == 0) // 此时为子进程的内容
        {
            cout<<"fork sucess, this is son process, pid="<<getpid()<<endl;
            while(i < 2)
            {
                i += 1;
                cout<<"this is son process, i = "<<i<<" pid = "<<getpid()<<endl;
                sleep(1);
            }
            exit(1); // 子进程运行完之后直接结束
        }
        else if(pid > 0)
        {
            //wait(NULL); // 先结束子进程中的内容
            return pid;
        }
        else
        {
            perror("error");
        }
    }
    int main()
    {
        pid_t p;
        for(int i = 0; i < 4; ++i)
        {
            create_fork();
        }
        int i = 0;
        while(i < 10)
        {
            i += 2;
            cout<<"this is father process i="<<i<<endl;
            sleep(2);
        }
        
        cout<<"process destroy"<<endl;
        return 0;
    }
    

    运行内容如下:

    fork sucess, this is son process, pid=6662
    this is son process, i = 1 pid = 6662
    fork sucess, this is son process, pid=6663
    this is son process, i = 1 pid = 6663
    this is father process i=2
    fork sucess, this is son process, pid=6664
    this is son process, i = 1 pid = 6664
    fork sucess, this is son process, pid=6665
    this is son process, i = 1 pid = 6665
    this is son process, i = 2 pid = 6662
    this is son process, i = 2 pid = 6663
    this is son process, i = 2 pid = 6664
    this is son process, i = 2 pid = 6665
    this is father process i=4
    this is father process i=6
    this is father process i=8
    this is father process i=10
    process destroy
    

    此时没有使用waitpid,接下来修改相应的内容,当指定的子进程结束之后主进程才会运行。

    修改每一个进程的睡眠时间,该内容让主进程保持第一个进程的pid,只有当第一个进程结束之后,主进程才会可以运行;

    #include <iostream>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <vector>
    using namespace std;
    pid_t create_fork(int time)
    {
        int i = 0;
        pid_t pid = fork();
        if(pid == 0) // 此时为子进程的内容
        {
            cout<<"fork sucess, this is son process, pid="<<getpid()<<endl;
            while(i < 2)
            {
                i += 1;
                cout<<"this is son process, i = "<<i<<" pid = "<<getpid()<<endl;
                sleep(time);
            }
            cout<<"fork destroy pid = "<<getpid()<<endl;
            exit(1); // 子进程运行完之后直接结束
        }
        else if(pid > 0)
        {
            //wait(NULL); // 先结束子进程中的内容
            return pid;
        }
        else
        {
            perror("error");
        }
    }
    int main()
    {
        pid_t p;
        for(int i = 0; i < 5; ++i)
        {
            pid_t temp = create_fork(i);
            if(i == 0)
            {
                p = temp;
            }
        }
        if(waitpid(p, NULL, 0))
        {
            int i = 0;
            while(i < 10)
            {
                i += 2;
                cout<<"this is father process i="<<i<<endl;
                sleep(2);
            }
        
        }
        cout<<"process destroy"<<endl;
        return 0;
    }
    

    结果如下,可以发现第一个经常destroy之后,主进程就开始了运行;

    fork sucess, this is son process, pid=6769
    this is son process, i = 1 pid = 6769
    this is son process, i = 2 pid = 6769
    fork destroy pid = 6769
    fork sucess, this is son process, pid=6770
    this is son process, i = 1 pid = 6770
    fork sucess, this is son process, pid=6771
    this is son process, i = 1 pid = 6771
    this is father process i=2
    fork sucess, this is son process, pid=6772
    this is son process, i = 1 pid = 6772
    fork sucess, this is son process, pid=6773
    this is son process, i = 1 pid = 6773
    this is son process, i = 2 pid = 6770
    fork destroy pid = 6770
    this is son process, i = 2 pid = 6771
    this is father process i=4
    this is son process, i = 2 pid = 6772
    fork destroy pid = 6771
    this is son process, i = 2 pid = 6773
    this is father process i=6
    fork destroy pid = 6772
    this is father process i=8
    fork destroy pid = 6773
    this is father process i=10
    process destroy
    

    接下来修改代码,也就是说当最后一个进程执行完之后,主进程才会执行;

    if(i == 4 ) // 将i 该为4
    {
        p = temp;
    }
    

    运行结果如下

    fork sucess, this is son process, pid=6823
    this is son process, i = 1 pid = 6823
    this is son process, i = 2 pid = 6823
    fork destroy pid = 6823
    fork sucess, this is son process, pid=6824
    this is son process, i = 1 pid = 6824
    fork sucess, this is son process, pid=6825
    this is son process, i = 1 pid = 6825
    fork sucess, this is son process, pid=6826
    this is son process, i = 1 pid = 6826
    fork sucess, this is son process, pid=6827
    this is son process, i = 1 pid = 6827
    this is son process, i = 2 pid = 6824
    this is son process, i = 2 pid = 6825
    fork destroy pid = 6824
    this is son process, i = 2 pid = 6826
    fork destroy pid = 6825
    this is son process, i = 2 pid = 6827
    fork destroy pid = 6826
    fork destroy pid = 6827
    this is father process i=2
    this is father process i=4
    this is father process i=6
    this is father process i=8
    this is father process i=10
    process destroy
    

    其中参数的使用

    int temp1 = WIFEXITED(flag);
    cout<<"WIFEXITED: 如果子进程正确结束返回一个非0值"<<temp1<<endl;
    if(temp1 != 0)
    {
        cout<<"如果子进程正常结束,返回子进程退出码"<<WEXITSTATUS(flag)<<endl;
    }
    if(WIFSIGNALED(flag) != 0)
    { // 不为0,表示因为捕获信号而终止
        cout<<"如果是因为捕获信号而终止的,返回相应的信号代码"<<WTERMSIG(flag)<<endl;
    }
    if(WIFSTOPPED(flag) != 0)
    {
        // 如果进程被暂停,返回一个非0值
        cout<<"如果因为暂停,返回相应的信号代码"<<WSTOPSIG(flag)<<endl;
    }
    

    可以加长子进程的工作时间,利用正常结束进程或者是
    kill -9 pid
    杀死相应的子进程来查看状态

    展开全文
  • 文章目录【Linux】进程控制(三):进程等待一、 进程等待的必要性二、 进程等待的方法2.1 wait 方法2.2 waitpid 方法三、获取子进程 status四、wait 的使用五、waitpid的使用六、守护进程和 nginx 反向代理器 ...
  • 进程等待的方式3.1 wait 函数3.2 waitpid 函数3.2.1获得子进程的status信息3.2.2 理解下waitpid内部是如何返回status的3.2.3 waitpid 的options参数的理解 1. 进程等待是什么 我们知道一般我们在父进程fork出一个子...
  • waitpid系统调用在Linux函数库中的原型是: #include /* 提供类型pid_t的定义 */ #include pid_t waitpid(pid_t pid,int *status,int options) 从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多...
  • wait()和waitpid()解析

    2021-01-17 13:20:01
    wait()函数说明wait(等待子进程中断或结束,相关函数 waitpid,fork)表头文件#include#include函数原型pid_t wait (int * status);函数说明参数 status 是一个整形指针。如果status不是一个空指针,则终止进程的终止...
  • waitpid()函数详解

    千次阅读 2022-02-09 14:25:39
    首先在了解waitpid()函数之前,我们需要先明确以下几个概念。 1.进程状态 从程序员的角度,我们可以认为进程总是处于下面三种状态之一: (1)运行。进程要么在CPU上执行,要么在等待被执行且最终会被内核调度。 ...
  • fork函数: 一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以...
  • C语言waitpid函数

    2022-02-17 23:30:29
    pid_t waitpid(pid_t pid, int *wstatus, int options); 回收指定进程号的进程 如果pid参数为0,那么就表示回收当前进程组的所有进程 如果pid为-1,就表示回收所有的子进程,不管是不是在同一个进程组 如果pid,...
  • wait和waitpid详解

    2020-12-18 07:23:35
    waitpid的函数原型是: #include #include pid_t waitpid(pid_t pid,int *status,int options) 从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为...
  • waitpid函数

    千次阅读 2019-03-27 00:58:46
    #include <sys/wait.h> #include <sys/types.h> pid_t waitpid(pid_t pid, int *status, int ...waitpid函数的第二个参数int *status跟wait函数的形参一样,且都是利用相同的宏函数来进一步获取...
  • options:提供控制waitpid()的选项,该选项是一个常量或由 | 链接的两个常量: 1.WONHANG: 即使进程没有终止,waitpid()也会立即返回,就是不会使父进程阻塞。 2.WUNTRACED: 如果子进程暂停执行,则waitpid()立即...
  • waitpid首先我们来了解一下所谓的僵尸进程,僵尸进程就是两个进程,一个父进程,一个子进程,其子进程终止后,0-3G的用户内存被回收,而3-4G的部分内存被回收,但是3-4G内存中的PCB等待父进程回收,若PCB未被父进程...
  • waitpid使用的一点问题

    2021-03-17 02:35:09
    原因在于system函数内部会经历fork()->exec()->waitpid()三个阶段,而我们的主函数中有处理SIGCHLD信号,里面还有waitpid操作。system本身的waitpid操作已经为子进程收尸过了,后面那个就会找不到子进程。处理的一...
  • 失败的原因主要有:没有子进程(errno设置为ECHILD),调用被某个信号中断(errno设置为EINTR)或选项参数无效(errno设置为EINVAL) 如果像这样调用waitpid函数:waitpid(-1, status, 0),这此时waitpid()函数就...
  • 文章目录wait()函数waitpid()函数wait() 和 waitpid() 用法和比较 wait()函数 #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status);   进程一旦调用了wait,就立即阻塞自己,由...
  • waitpid函数的介绍

    2021-08-28 11:00:30
    1.wait和waitpid的差别 (1)基本功能是一样的,都是用来回收子进程的 (2)waitpid可以回收指定PID的子进程 (3)waitpid可以阻塞式和非阻塞式两种工作模式 2.waitpid原型介绍 pid_t waitpid(pid_t pid, int *...
  • Linux wait()/waitpid()

    2022-03-03 21:13:58
    这里写目录标题wait()waitpid() wait() 使用wait()函数与waitpid()函数让父进程回收子进程的系统资源,两个函数的功能大致类似,waitpid()函数的功能要比wait()函数的功能更多。 所需头文件: #include<sys/types...
  • waitpid 函数2.1 函数原型2.2 函数功能2.3 函数参数2.4 函数返回值2.5 使用宏获取传出参数所携带的状态信息2.6 代码示例 0. 背景 进程在终止时会关闭所有文件描述符、释放在用户区分配的内存,但是在内核区该进程的...
  • pid_t waitpid(pid_t pid, int *status, int options) pid: pid > 0 只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了, 只要指定的子进程还没有结束,waitpid就会一直等下去 pid == -1 等待...
  • 简要介绍( waitpid() ): 1.waitpid(): pid_t waitpid(pid_t pid,int *status,int options); 1> 参数 pid 一般是进程的 pid, 但也会有其它取值。 参数 pid 的取值及其意义分别如下: > 0 时, 回收指定 id 的子进程...
  • 进程waitpid()的用法

    2021-11-04 18:11:27
    从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。下面我们就来详细介绍一下这两个参数: pid 从参数的名字...
  • waitpid()回收子进程

    2022-03-22 13:28:57
    如果在调用waitpid()函数时,当指定等待的子进程已经停止运行或结束了,则waitpid()会立即返回; 但是如果子进程还没有停止运行或结束,则调用waitpid()函数的父进程则会被阻塞,暂停运行。 .

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,538
精华内容 14,215
关键字:

waitpid

友情链接: PB_carm.rar