Linux系统下进程回收--waitpid函数

Posted nakjima

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux系统下进程回收--waitpid函数相关的知识,希望对你有一定的参考价值。

waitpid函数的作用和wait函数的作用相同,只是有些参数不同。
在Linux终端下输入命令:man 2 waitpid查看waitpid函数的具体描述:

SYNOPSIS
       #include <sys/types.h>
       #include <sys/wait.h>

        pid_t waitpid(pid_t pid, int *wstatus, int options);
            作用:回收指定进程号的子进程,可以设置非阻塞(默认阻塞)。
            参数:
                pid_t pid:
                    < -1:回收某个进程组的组id的绝对值,回收指定进程组的子进程
                    -1:回收所有子进程,相当于wait()        //最常用
                    0:回收当前进程组的所有子进程
                    > 0:某个子进程的id

                The value of pid can be:

                    < -1   meaning wait for any child process whose process group ID is equal to the absolute value of pid.

                    -1     meaning wait for any child process.

                    0      meaning wait for any child process whose process group ID is equal to that of the calling process.

                    > 0    meaning wait for the child whose process ID is equal to the value of pid.
                
                
                int *wstatus:进程退出时的状态信息,传入的是一个int类型的地址,传出参数。
                    返回值:
                        - 成功:返回被回收的子进程的id
                        - 失败:返回-1(调用函数失败,或者所有子进程都被回收)

                int options:设置阻塞或非阻塞
                    0:阻塞
                    WNOHANG:非阻塞
            返回值:
                > 0:返回子进程的id
                = 0:options=WNOHANG,表示还有子进程没有退出(在非阻塞的情况下)
                -1 :错误,或者没有子进程

        
        WIFEXITED(wstatus)
              returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by  returning  from
              main().

       WEXITSTATUS(wstatus)
              returns  the  exit  status of the child.  This consists of the least significant 8 bits of the status argument
              that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main().
              This macro should be employed only if WIFEXITED returned true.

       WIFSIGNALED(wstatus)
              returns true if the child process was terminated by a signal.

下面是waitpid函数的简单案例:

#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <unistd.h>
#include <stdlib.h>

using std::cout;
using std::endl;

namespace waitpid_test
    void test()

        //有一个父进程创建5个子进程
        pid_t pid;

        //创建5个子进程
        for(int i=0;i<5;++i)
            pid = fork();
            if(pid == 0)
                break;
        

        if(pid>0)
            //父进程
            while(1)
                cout<<"parent,pid:"<<getpid()<<endl;
                sleep(1);
                //int ret = wait(NULL);
                int st;
                
                //阻塞
                //int ret = waitpid(-1,&st,0);

                //非阻塞
                int ret = waitpid(-1,&st,WNOHANG);


                if(ret == -1)
                    break;
                
                else  if(ret == 0)
                    //说明还有子进程存在
                    continue;
                else if(ret > 0)
                
                    if(WIFEXITED(st))
                        //是不是正常退出
                        cout<<"退出的状态码:"<<WEXITSTATUS(st)<<endl;
                    if(WIFSIGNALED(st))
                        //是不是异常终止
                        cout<<"被哪个信号干掉了:"<<WTERMSIG(st)<<endl;
                    cout<<"child has died,pid:"<<ret<<endl;
                

                 
                
                
            
        
        else if(pid == 0)
            //子进程
            while(1)
            cout<<"child, pid:"<<getpid()<<endl;
            sleep(1);    
            
            exit(0);
        
    



int main()

    //wait_test::test();
    waitpid_test::test();
    return 0;

Linux下异步回收子进程

背景

  我们知道,当一个进程fork出子进程后,没有对子进程进行回收,那么子进程运行完之后的状态会变为僵尸进程.

  我们可以通过wait和waitpid来回收子进程,防止僵尸进程的出现.

  但是wait和waitpid,要么以阻塞方式运行,要么以轮询方式运行,都极大的占用了CPU资源.

  本文将介绍,父进程如何通过异步操作回收子进程!

原理

  在每个子进程运行完成之后,都会向父进程发出SIGCHLD信号.而在默认情况下,父进程会忽略掉该信号.因此,我们只需要对SIGCHLD信号进行捕捉,即可异步对子进程进行回收.

核心代码1

void waitChild1(int sig){
	int status = 0;
	if(waitpid(-1,&status,WNOHANG)>0){
		printf("the child exit status is %d\n",WEXITSTATUS(status));
	}else{
		printf("failure!\n");
	}
}

   因此,我们通过waitpid,对所有产生SIGCHLD信号的子进程都进行回收.

  好像很简单的样子嘛!!!

  对的,就是这么简单!

  但是上述代码存在BUG,如果存在很多子进程都同时退出,那么就会同时向父进程发送信号.

  而我们知道,由于受到位图的限制,常规信号出现多次时,只计算一次.因此,如果同时存在子进程出现时,以上代码肯定不能把所有子进程都回收掉!

  所以,我们需要对代码进行加强!!!!

升级版核心代码

void waitChild2(int sig){
	int status = 0;
	pid_t pid = 0;
	while((pid = waitpid(-1,&status,WNOHANG)) >0){
		printf("%d‘s exit status is %d\n",pid,WEXITSTATUS(status));
	}
}

   正如上述代码,改动的其实很简单,只需把if改成while即可!

完整版代码

////////////////////////////////////
//文件说明:wait.c
//作者:高小调
//创建时间:2017年06月27日 星期二 20时45分16秒
//开发环境:Kali Linux/g++ v6.3.0
////////////////////////////////////
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
void waitChild1(int sig){
	int status = 0;
	if(waitpid(-1,&status,WNOHANG)>0){
		printf("the child exit status is %d\n",WEXITSTATUS(status));
	}else{
		printf("failure!\n");
	}
}
void waitChild2(int sig){
	int status = 0;
	pid_t pid = 0;
	while((pid = waitpid(-1,&status,WNOHANG)) >0){
		printf("%d‘s exit status is %d\n",pid,WEXITSTATUS(status));
	}
}
int main(){
	signal(SIGCHLD,waitChild2);
	int pid = fork();
	if(pid == 0){
		//child
		printf("i am child,my pid is %d\n",getpid());
		exit(1);
	}
	pid = fork();
	if(pid == 0){
		//child
		printf("i am child,my pid is %d\n",getpid());
		exit(1);
	}
	pid = fork();
	if(pid == 0){
		//child
		printf("i am child,my pid is %d\n",getpid());
		exit(1);
	}
	pid = fork();
	if(pid == 0){
		//child
		printf("i am child,my pid is %d\n",getpid());
		exit(1);
	}
	while(1){
		printf("i am father,my pid is %d\n",getpid());
		sleep(5);
	}
	return 0;
}

 

以上是关于Linux系统下进程回收--waitpid函数的主要内容,如果未能解决你的问题,请参考以下文章

Linux提高:僵尸进程

Linux提高:僵尸进程

linux下system函数调用shell命令后,怎样让主进程等子进程返回后,接着执行

waitpid函数的使用方式

linux 进程等待 wait waitpid

20155335 俞昆 第六周《信息安全系统设计基础》