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);
}