wait 和 waitpid 函数

    当一个进程正常终止或异常终止时,内核就会向其父进程发送 SIGCHLD 信号,父进程对这种信号的默认动作是忽略它。可以使用 wait 和 waitpid 函数来获取子进程的终止状态。
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
                  /* 两个函数返回值:若成功,返回进程 ID;否则,返回 0 或 -1 */

    通常调用这两个函数时:
    * 如果其所有子进程都还在运行,则阻塞。
    * 如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态后立即返回。
    * 如果它没有任何子进程,则立即出错返回。
    这两个函数的区别如下。
    (1)在一个子进程终止前,wait 使其调用者阻塞,而 waitpid 有一选项可使调用者不阻塞。
    (2)waitpid 并不一定要等待在其调用之后的第一个终止子进程,它有若干个可以控制它所等待的进程的选项。
    (3)对于 wait,其唯一的出错是调用进程没有子进程(函数调用被一个信号中断时也可能返回另一种出错)。但对于 waitpid,如果指定的进程或进程组不存在,或者 pid 指定的进程不是调用进程的子进程,都可能出错。
    它们的参数 statloc 如果不是一个空指针,则子进程的终止状态就存放在它所指向的单元内。如果不关心终止状态,则可将其指定为空指针。依照传统,返回的整型状态字是由实现定义的,其中某些位表示退出状态(正常返回),其他位则指示信号编号(异常返回),有一位指示是否产生了 core 文件等。终止状态可用 <sys/wait.h> 中的各个宏来查看,下表中 4 个互斥的宏可用来取得进程终止的原因。基于这 4 个宏中哪一个为真,就可选用其它宏来取得退出状态、信号编号等(很多平台都支持 WCOREDUMP 宏,但如果定义了 _POSIX_C_SOURCE 常量,有些平台就隐藏该定义)。

    下面这个程序就是利用这 4 个宏来打印进程终止状态的说明。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

void pr_exit(int status){
	if(WIFEXITED(status))
		printf("normal terminaltion, exit status = %d\n",
				WEXITSTATUS(status) );
	else if(WIFSIGNALED(status))
		printf("abnormal termination, signal number = %d%s\n",
				WTERMSIG(status),
			#ifdef WCOREDUMP
				WCOREDUMP(status)?"(core file generated)": "");
			#else
				"");
			#endif
	else if(WIFSTOPPED(status))
		printf("child stopped, signal number = %d\n",
				WSTOPSIG(status));
}

int main(void){
	pid_t	pid;
	int		status;

	if((pid=fork()) < 0){
		printf("1st fork error\n");
		exit(2);
	}else if(pid == 0)		// child
		exit(7);
	if(wait(&status) != pid){	// wait for child
		printf("1st wait error\n");
		exit(2);
	}
	pr_exit(status);		// and print its status

	if((pid=fork()) < 0){
		printf("2nd fork error\n");
		exit(2);
	}else if(pid == 0)
		abort();		// generates SIGABRT, signal number is 6
	if(wait(&status) != pid){
		printf("2nd wait error\n");
		exit(2);
	}
	pr_exit(status);

	if((pid=fork()) < 0){
		printf("3rd fork error\n");
		exit(2);
	}else if(pid == 0)
		status /= 0;		// generates SIGFPE, signal number is 8
	if(wait(&status) != pid){
		printf("3rd wait error\n");
		exit(2);
	}
	pr_exit(status);

	exit(0);
}

    运行结果如下。
$ ./exitStatusDemo.out 
normal terminaltion, exit status = 7
abnormal termination, signal number = 6(core file generated)
abnormal termination, signal number = 8(core file generated)
$ 

    如果一个进程有几个子进程,那么只要有一个子进程终止,wait 就返回。如果要等待一个指定的进程终止,waitpid 函数就派上用场了。对于 waitpid 函数中的 pid 参数的作用解释如下。
    * pid == -1:等待任一子进程,此时等效于 wait。
    * pid > 0:等待进程 ID 为 pid 的子进程。
    * pid == 0:等待组 ID 等于调用进程组 ID 的任一子进程。
    * pid < -1:等待组 ID 等于 pid 绝对值的任一子进程。
    参数 options 可以进一步控制 waitpid 的操作。此参数可以为 0 或者是下表中各常量按位或运算的结果(FreeBSD 8.0 和 Solaris 10 支持另一个非标准的可选常量 WNOWAIT,它使系统将终止状态已由 waitpid 返回的进程保持在等待状态,这样它可被再次等待)。

    如果一个进程 fork 了一个子进程,但不要它等待子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一要求的诀窍就是调用 fork 两次,正如下列这个程序所示。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void){
	pid_t		pid;

	if((pid=fork()) < 0){
		printf("1st fork error\n");
		exit(2);
	}else if(pid == 0){		// first child
		if((pid=fork()) < 0){
			printf("2nd fork error\n");
			exit(2);
		}else if(pid > 0){
			exit(0);	// parent from second fork == first child
		}

		/* We're the second child; our parent becomes init as soon as
		 * our real parent calls exit() in the statement above. Here's
		 * where we'd continue executing, knowing that when we're 
		 * done, init will reap our status.
		 */
		sleep(2);
		printf("second child, parent pid=%ld\n", (long)getppid());
		exit(0);
	}

	if(waitpid(pid, NULL, 0) != pid){	// wait for first child
		printf("waitpid error\n");
	}

	/* We're the parent(the original process); we continue executing,
	 * knowing that we're not the parent of the second child.
	 */
	exit(0);
}

    执行结果:
$ ./fork2timesDemo.out
$ second child, parent pid = 1

    注意,这里当原先的进程(即 exec 本程序的进程)终止时,shell 打印其提示符,因为这在第二个子进程打印其父进程 ID 之前发生。
    另外,Single UNIX Specification 包含了另一个取得进程终止状态的函数----waitid,此函数类似于 waitpid,但提供了更多的灵活性。
#include <sys/wait.h>
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
                                   /* 返回值:若成功,返回 0;否则,返回 -1 */

    waitid 也允许一个进程指定要等待的子进程,但它使用两个单独的参数表示要等待的进程所属的类型,而不是将此与进程 ID 或进程组 ID 组合成一个参数。id 参数的作用与 idtype 的值相关。该函数支持下表中的这几个 idtype 类型。

    options 参数是下表中各标志的按位或运算,这些标志指示调用者关注哪些状态变化。

    注意,WCONTINUED、WEXITED 和 WSTOPPED 这 3 个常量之一必须指定。
    infop 参数是指向 siginfo 结构的指针,该结构包含了造成子进程状态改变有关信号的详细信息。
    大多数 UNIX 系统实现还提供了 wait3 和 wait4 函数,它们提供的功能比上面几个 wait 函数提供的要多一个,这与附加参数有关,该参数允许内核返回由终止进程及其所有子进程使用的资源情况。资源统计信息(不同于资源限制)包括用户 CPU 时间总量、系统 CPU 时间总量、缺页次数、接收到信号的次数等,有关细节可查阅 getrusage(2) 手册页。
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

pid_t wait3(int *statloc, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);
                        /* 两个函数返回值:若成功,返回进程 ID;否则,返回 -1 */

猜你喜欢

转载自aisxyz.iteye.com/blog/2391519