5 wait和waitpid函数
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。
5.1调用wait或waitpid的进程可能会:
阻塞(如果其子进程都还在运行)
带子进程的终止状态立即返回
出错立即返回
5.2wait和waitpid的区别:
在一个子进程终止前, wait 使其调用者阻塞,而 waitpid 有一选择项,可使调用者不阻塞。
waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的进程。
代码示例:
#include "ourhdr.h" #include <sys/wait.h> void pr_exit(int status) { if (WIFEXITED(status)) printf("normal termination, 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) err_sys("fork error"); else if (pid == 0) /* child */ exit(7); 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 */ exit(0); }
打印结果:
[root@rac1 fork]# gcc wait.c wait.c: In function ‘main’: wait.c:48: warning: division by zero [root@rac1 fork]# ./a.out normal termination, exit status = 7 abnormal termination, signal number = 6 abnormal termination, signal number = 8
5.3如果一个进程要 f o r k一个子进程,但不要求它等待子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一要求的诀窍是调用 f o r k两次。
代码示例:
#include "ourhdr.h" #include <sys/wait.h> #include <sys/types.h> int main(void) { pid_t pid; if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* first child */ if ((pid = fork()) < 0) err_sys("fork error"); 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 = %d\n", getppid()); exit(0); } if (waitpid(pid, NULL, 0) != pid) /* wait for first child */ err_sys("waitpid error"); /* * We're the parent (the original process); we continue executing, * knowing that we're not the parent of the second child. */ exit(0); }
打印结果:
[root@rac1 fork]# ./a.out [root@rac1 fork]# second child, parent pid = 1
6 wait3 和wait4函数
7 竞态条件
当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,则我们认为这发生了竞态条件
在父、子进程的关系中,常常出现下述情况。在 f o r k之后,父、子进程都有一些事情要做。例如,父进程可能以子进程 I D更新日志文件中的一个记录,而子进程则可能要为父进程创建一个文件
代码示例:
tellwait.h
#include <sys/types.h> static int pfd1[2], pfd2[2]; void TELL_WAIT(void) { if (pipe(pfd1) < 0 || pipe(pfd2) < 0) err_sys("pipe error"); } void TELL_PARENT (pid_t pid) { if (write(pfd2[1], "c", 1) != 1) err_sys("write error"); } void WAIT_PARENT(void) { char c; if (read(pfd1[0], &c, 1) != 1) err_sys("read error"); if (c != 'p') err_quit("WAIT_PARENT: incorrect data"); } void TELL_CHILD(pid_t pid) { if (write(pfd1[1], "p", 1) != 1) err_sys("write error"); } void WAIT_CHILD(void) { char c; if (read(pfd2[0], &c, 1) != 1) err_sys("read error"); if (c != 'c') err_quit("WAIT_CHILD: incorrect data"); }
tell.c
#include "ourhdr.h" static void charatatime(char *); int main(void) { pid_t pid; TELL_WAIT(); if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { WAIT_PARENT(); /* parent goes first */ charatatime("output from child\n"); } else { charatatime("output from parent\n"); TELL_CHILD(pid); } exit(0); } static void charatatime(char *str) { char *ptr; int c; setbuf(stdout, NULL); /* set unbuffered */ for (ptr = str; (c = *ptr++) != 0; ) putc(c, stdout); }
运行结果:
[root@rac1 fork]# ./tell output from parent [root@rac1 fork]# output from child [root@rac1 fork]#
8 exec函数
用f o r k函数创建子进程后,子进程往往要调用一种 e x e c函数以执行另一个程序。
当进程调用一种 e x e c函数时,该进程完全由新程序代换,而新程序则从其 m a i n函数开始执行
#include <unistd.h> int execl(const char * pathname, const char * arg0, ... /* (char *) 0 */); int execv(const char * pathname, char *const argv [] ); int execle(const char * pathname, const char * arg0, .../* (char *)0, char *const envp [] */); int execve(const char * pathname, char *const argv [], char *const envp [] ); int execlp(const char * filename, const char * arg0, ... /* (char *) 0 */); int execvp(const char * filename, char *const argv [] ); 六个函数返回:若出错则为- 1,若成功则不返回
代码示例:
#include "ourhdr.h" #include <sys/wait.h> char *env_init[] = { "USER=unknown", "PATH=/tmp", NULL }; int main(void) { pid_t pid; if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* specify pathname, specify environment */ if (execle("/usr/local/bin/python", "python", "myarg1", "--", (char *)0, env_init) < 0) err_sys("execle error"); } if (waitpid(pid, NULL, 0) < 0) err_sys("wait error"); if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* specify filename, inherit environment */ if (execlp("python", "", "myarg2", (char *)0) < 0) err_sys("execlp error"); } exit(0); }