wiat()与waitpid()

          在创建进程后,为了避免僵尸进程,资源的浪费和进程数量的空闲占据,则需要对进程资源进行回收,这里主要看看wait和waitpid两个进程的功能与区别!!

一、pid_t wait(int *status)

        作用(1):清理结束的子进程资源或者得到进程执行后的返回信息;

    进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出(主要是回收资源),如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

        参数status:用来保存被收集进程退出时的一些状态,因为我们需要确定我们的进程是否执行成功。它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:
pid = wait(NULL);

如果不是NULL,wait会把子进程退出时的一些状态存入其中,这是一个整数值(int),指出子进程退出时的状态。

最常用的两个:

   WIFEXITED(status):       若为正常终止, 则为真. 此时可执行 WEXITSTATUS(status): 取子进程传送给exit或_exit参数的低8位.;当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说, WIFEXITED返回0,这个值就毫无意义。
    WIFSIGNALED(status): 若为异常终止, 则为真.此时可执行 WTERMSIG(status): 取使子进程终止的信号编号;

   WIFSTOPPED(status):  若为当前暂停子进程, 则为真. 此时可执行 WSTOPSIG(status): 取使子进程暂停的信号编号;

  返回值:
  成功:wait会返回被收集的子进程的进程ID;
  失败:如果调用进程没有子进程,调用就会失败,此时wait返回-1。同时errno被置为ECHILD。

   程序实例1:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
main()
{
        pid_t pc,pr;
        pc=fork();
        if(pc<0) //判断进程是否创建成功;
               printf("error ocurred!/n");
       else if(pc==0)//如果是子进程 
       { 
               printf("This is child process with pid of %d\n",getpid());
               sleep(5); //让子进程等待5S;
       }
        else
       { //如果是父进程 
              pr=wait(NULL); //父进程将在这里等待5S,等待子进程的结束;
              printf("I catched a child process with pid of %d\n",pr);
        }
       exit(0);//正常退出;
}

运行结果:

wiat()与waitpid()_第1张图片

      可以明显感觉到打印第二句的时候,程序等了5S,只要调用wait(),父进程就会等子进程结束后才能结束,才能捕获到子进程的状态,才能正常退出。事实上,不管我们设置多长时间,父进程都会一直等待下去。

程序实例2:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
main()
{
       int status;
       pid_t pc,pr;
        pc=fork();
        if(pc<0) //创建进程出错;
               printf("error ocurred!\n");
        else if(pc==0)
        { //子进程
               printf("This is child process with pid of %d.\n",getpid());
               exit(10); //子进程成功调用后返回10,利用这个返回值看子进程是否运行顺利;
        }
        else
        { //父进程 
               pr=wait(&status);
               if(WIFEXITED(status)){ //如果WIFEXITED返回非零值,代表子进程执行成功;
                   printf("the child process %d exit normally.\n",pr);
                   printf("the return code is %d.\n",WEXITSTATUS(status));
                       //再次判断是否执行了我们想要执行的代码和位置;
               }else // 如果WIFEXITED返回零 ,则说明子进程没有执行成功;
                   printf("the child process %d exit abnormally.\n",pr);
        }
}

运行结果:


通过这个程序我们可以用子进程的返回值来判断子进程是否顺利执行并返回;

程序实例3:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
main()
{
	pid_t pc, pr;
	pc=fork();
	if(pc<0) // 如果fork出错 
		printf("Error occured on forking.\n");
	else if(pc==0){ //子进程
		sleep(5); // 睡眠5秒 
		exit(0);
	}
	// 父进程 
	do{
		pr=waitpid(pc, NULL, WNOHANG); // 使用了WNOHANG参数,waitpid不会在这里等待,是非阻塞的wait(); 
		if(pr==0)
		{ // 如果没有收集到子进程,收集到会返回PID
			printf("No child exited\n");
			sleep(1);
		}
		}while(pr==0);// 没有收集到子进程,就回去继续尝试;
		if(pr==pc)//找到一个子进程并回收;
			printf("successfully get child %d\n", pr);
		else
			printf("some error occured\n");
}
运行结果:

wiat()与waitpid()_第2张图片

    从结果可以看出,如果加入了WNOHANG参数后,父进程就会等待子进程的结束,但是不会一直等待下去,而是间断的去轮询,这样提高了程序的运行效率。不是死等!!

    作用(2):进程同步问题;

    当fork调用成功后,父子进程各做各的事情,但当父进程的工作告一段落,需要用到子进程的结果时,它就停下来调用wait,一直等到子进程运行结束,然后利用子进程的结果继续执行,这样就圆满地解决了我们提出的进程同步问题。

程序实例:

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
main()
{
	pid_t pc, pr;
	int status,a,b,c,sum;
	a=10;
	b=2;
	pc=fork();

	if(pc<0)
		printf("Error occured on forking.\n");
	else if(pc==0)
	{
		//子进程的工作,算C的值;
		printf("this is child process,ID:%d,PID:%d.",getpid(),getppid());
		c=a+b;
		exit(c);
	}
	else
	{
		// 父进程的工作 
		pr=wait(&status);
		//利用子进程的结果 C;
		if(WIFEXITED(status))
		//如果WIFEXITED返回非零值,代表子进程执行成功;
		{
		        printf("this is father process,ID:%d.\n",getpid());
			sum=WEXITSTATUS(status);//得到子进程的结果;
			printf("%d\n",sum);//输出子进程的结果;
		}
		else
		{
			printf("the child process %d exit abnormally.\n",pr);
		}
	}
}
运行结果:

wiat()与waitpid()_第3张图片

       这个程序就可以说明,父进程的工作依赖于子进程的执行结果,所以必须等待子进程的结束;
二、pid_t waitpid(pid_t pid,int *status,int options);

   wait&waitpid 区别 :

    waitpid提供了wait函数不能实现的3个功能: 

    (1)、waitpid等待特定的子进程, 而wait则返回任一终止状态的子进程; 
    (2)、waitpid提供了一个wait的非阻塞版本; 
    (3)、waitpid支持作业控制(以WUNTRACED选项). 用于检查wait和waitpid两个函数返回终止状态的宏: 这两个函数返回的子进程状态都保存在status指针中, 用以下3个宏可以检查该状态: 

       WIFEXITED(status): 若为正常终止, 则为真. 此时可执行 WEXITSTATUS(status): 取子进程传送给exit或_exit参数的低8位. 
       WIFSIGNALED(status): 若为异常终止, 则为真.此时可执行 WTERMSIG(status): 取使子进程终止的信号编号.
       WIFSTOPPED(status): 若为当前暂停子进程, 则为真. 此时可执行 WSTOPSIG(status): 取使子进程暂停的信号编号
     从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数 pid和options,从而为我们编程提供了另一种更灵活的方式。下面我们就来详细介绍一下这两个参数:
   第一个参数:pid:从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。
   pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
   pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
   pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
   pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
   第二个参数:status,保存收集进程退出时的一些状态;正常退出或非正常退出;
    第三个参数:options提供了一些额外的选项来控制waitpid,目前linux支持WNOHANG和WUNTRANCED这两个选项,是两个常数;
      WNHANG:即使没有子进程退出,他也会立即返回,不会向wait那样永远等下去,也就是一个无阻塞的wait;
       例如:
    ret=waitpid(-1,NULL,WNOHANG);//无阻塞的wait();
    pr=waitpid(pc, NULL, 0);与pr=wait(NULL)就完全一模一样了;
     WUNTRACED:它涉及到一些跟踪调试信息;
      其实wait就是经过包装的waitpid:
    static inline pid_t wait(int * wait_stat)
   {
       return waitpid(-1,wait_stat,0);
   }
返回值和错误
waitpid的返回值比wait稍微复杂一些,一共有3种情况:
          1、当正常返回的时候,waitpid返回收集到的子 进程的进程ID
          2、如果设置了选项 WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
          3、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;
问题:如何使父进程调用wait()和waitpid()避免挂起的尴尬,还可以顺利收回子进程的资源,避免僵尸进程:
     (1). 可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。
     (2).如果父进程不关心子进程什么时候结束,那么可以用 signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后, 内核会回收,并不再给父进程发送信号
      (3). fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出, 那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做(立马就回收,是孙进程编程孤儿进程)。

博文参考:

http://blog.csdn.net/kevinhg/article/details/7001719

http://blog.sina.com.cn/s/blog_590be5290100nc51.html

http://blog.chinaunix.net/uid-25365622-id-3045460.html

感谢博主的分享!

你可能感兴趣的:(进程,wait,waitpid)