不管进程如何终止,最后都会执行内核中的同一段代码,为相应的进程关闭所有打开描述符,释放它所使用的存储器。无论进程如何终止,我们都希望该进程能够通知其父它是如何终止的,对于exit,_exit,_Exit(一种情况),将其退出状态作为参数传送给函数(exit(3)),对于异常终止(另一种情况),内核产生一个指示其异常终止原因的终止状态。这两种情况,该进程父进程都能用wait或waitpid函数取得其终止状态。
退出状态与终止状态有所区别。
僵死进程是,一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍占用的资源)的进程。
当一个进程正常或异常终止时,内核向父进程发送SIGCHLD信号,该信号是内核向父进程发的异步通知,父进程可以忽略它,也可定义一个函数(信号处理程序)来处理这个信号。默认是忽略。父调用wait/waitpid时:
- 如果其所有子进程都在运行,则阻塞。
- 如果一个子终止,则父取得子终止状态,父wait或者waitpid立即返回。
- 如果父没有任何子,立即返回。
如果父由于接到SIGCHLD而调用wait,则可期待wait立即返回。
waitpid可选择等待终止的子进程。
进程终止状态在<sys/wait.h>/usr/include/sys/wait.h
UNIX系统信号
signum.h
如果一个子进程已经终止,并且是一个僵死进程,则wait立即返回并取得该子进程状态(就是说父调用wait的时候,其儿子中已经有一个是僵死进程了,因为子终止之前,父没有调用wait,并进行收尸工作),否则wait阻塞到任一子进程终止。
// proc/wait1.c 8-4 #include "apue.h" #include <sys/wait.h> int main(void) { pid_t pid; int status; if ((pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) /* child */ exit(7); // terminate normally if (wait(&status) != pid) /* wait for child */ err_sys("wait error"); pr_exit(status); /* and print its status */ if ((pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) /* child */ abort(); /* generates SIGABRT */ if (wait(&status) != pid) /* wait for child */ err_sys("wait error"); pr_exit(status); /* and print its status */ if ((pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) /* child */ status /= 0; /* divide by 0 generates SIGFPE */ if (wait(&status) != pid) /* wait for child */ err_sys("wait error"); pr_exit(status); /* and print its status */ return 0; }
// lib/prexit.c #include "apue.h" #include <sys/wait.h> void pr_exit(int status) { // two condition of a process terminate: // 1, child invoke exit. // 2, child terminate abnormally. if (WIFEXITED(status)) // exited printf("normal termination, exit status = %d ", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) // signaled, and parent has no function handle this signal printf("abnormal termination, signal number = %d%s ", WTERMSIG(status), #ifdef WCOREDUMP WCOREDUMP(status) ? " (core file generated)" : ""); #else ""); #endif else if (WIFSTOPPED(status)) printf("child stopped, signal number = %d ", WSTOPSIG(status)); }
all: shell1 shell2 fork1 vfork1 wait1 shell1: shell1.c g++ -g -Wall shell1.c ../lib/libapue.a -I ../include -o shell1 shell2: shell2.c g++ -g -Wall shell2.c ../lib/libapue.a -I ../include -o shell2 fork1: fork1.c g++ -g -Wall fork1.c ../lib/libapue.a -I ../include -o fork1 vfork1: vfork1.c g++ -g -Wall vfork1.c ../lib/libapue.a -I ../include -o vfork1 wait1: wait1.c g++ -g -Wall wait1.c ../lib/libapue.a -I ../include -o wait1 clean: rm shell1 shell2 fork1 vfork1 wait1
调用fork两次以避免僵死进程
// proc/fork2.c 8-5 #include "apue.h" #include <sys/wait.h> int main(void) { printf("file %s, line %d parent, getpid() = %d ", __FILE__, __LINE__, getpid()); fflush(stdout); pid_t pid; if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* first child */ printf("file %s, line %d first child, getpid() = %d ", __FILE__, __LINE__, getpid()); fflush(stdout); if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid > 0) { printf("first child, going to sleep(2) "); sleep(2); printf("first child, after sleep(2), begin exiting "); 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. */ printf("file %s, line %d, child 2, getpid() = %d ", __FILE__, __LINE__, getpid()); printf("second child, going to sleep(5) "); sleep(5); printf("second child, after sleep(5) "); printf("second child, parent pid = %d, parent id is 1, which means my parent process is init ", getppid()); exit(0); } if (waitpid(pid, NULL, 0) != pid) /* wait for first child */ err_sys("waitpid error"); else printf("after sleep(2), first child exit caught by parent (waitpid) "); printf("file %s, line %d, getpid() = %d ", __FILE__, __LINE__, getpid()); /* * We're the parent (the original process); we continue executing, * knowing that we're not the parent of the second child. */ exit(0); }
all: shell1 shell2 fork1 vfork1 wait1 fork2 shell1: shell1.c g++ -g -Wall shell1.c ../lib/libapue.a -I ../include -o shell1 shell2: shell2.c g++ -g -Wall shell2.c ../lib/libapue.a -I ../include -o shell2 fork1: fork1.c g++ -g -Wall fork1.c ../lib/libapue.a -I ../include -o fork1 fork2: fork2.c g++ -g -Wall fork2.c ../lib/libapue.a -I ../include -o fork2 vfork1: vfork1.c g++ -g -Wall vfork1.c ../lib/libapue.a -I ../include -o vfork1 wait1: wait1.c g++ -g -Wall wait1.c ../lib/libapue.a -I ../include -o wait1 clean: rm shell1 shell2 fork1 vfork1 wait1 fork2