linux多进/线程编程(3)——wait、waitpid函数和孤儿、僵尸进程
当使用fork创建多个进程后,需要解决子进程回收的问题。wait和waitpid函数就是做这个工作的。
假设子进程没有合理的回收,可能会带来两个问题:
1.孤儿进程(父进程挂了,子进程活着),孤儿进程会被init进程回收,可以理解其没有危害,不会占用资源。
2.僵尸进程(子进程挂了,父进程活着),僵尸进程是当父进程活着时,子进程没有其他进程帮忙回收后产生的“有害进程”,所以僵尸必须要回收,否则僵尸进程太多了会占用系统资源。此时就需要用到wait或waitpid函数。
wait和waitpid的区别在于:
wait函数是阻塞的,必须等到子进程被回收才会执行wait之后的代码;
waitpid可以设置为非阻塞的,不过非阻塞可能带来新的问题,子进程还没有回收waitpid这句代码就走完了(一般会写循环,通过判断返回值解决)。
wait:
WAIT(2) Linux Programmer's Manual WAIT(2) NAME wait, waitpid, waitid - wait for process to change state SYNOPSIS #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);
返回值:
RETURN VALUE wait(): on success, returns the process ID of the terminated child; on error, -1 is returned. waitpid(): on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more child(ren) specified by pid exist, but have not yet changed state, then 0 is returned. On error, -1 is returned.
waitpid的参数:
The child state changes to wait for are specified by ORing one or more of the following flags in options: WEXITED Wait for children that have terminated. WSTOPPED Wait for children that have been stopped by delivery of a signal. WCONTINUED Wait for (previously stopped) children that have been resumed by delivery of SIGCONT. The following flags may additionally be ORed in options: WNOHANG As for waitpid(). WNOWAIT Leave the child in a waitable state; a later wait call can be used to again retrieve the child status information.
wait用法示例:
pid_t pid = fork(); if (pid == 0) { //while (true) { //sleep(1); //} printf("i am child, i will die\n"); sleep(2); //return 101; exit(2); } else if (pid > 0) { printf("i am parent, wait for a child die\n"); int status { 0 }; pid_t wait_pid = wait(&status);//1,阻塞等待(子进程死亡);2,回收子进程资源(避免僵尸进程);3,查看状态(死亡原因) printf("wait ok, wait_pid=%d, pid=%d\n", wait_pid, pid); if (WIFEXITED(status)) { printf("child exit with %d\n", WEXITSTATUS(status)); } if (WIFSIGNALED(status)) { printf("child killed by %d\n", WTERMSIG(status)); } while (true) { sleep(1); } }
waitpid用法示例:
pid_t pid = fork(); int ret { 0 }; if (pid == 0) { printf("i am child, pid=%d\n", getpid()); sleep(2); exit(101); } else if (pid > 0) { printf("i am parent, self=%d, child_pid=%d\n", getpid(), pid); //不阻塞可能会带来一个问题,子进程还没回收,这句代码就走完了,所以要加循环(让代码阻塞在这里。。这。。。那还不如直接用wait????) //WNOHANG: 不阻塞 int status { 0 }; while ((ret = waitpid(-1, &status, WNOHANG)) == 0) { sleep(1); } printf("ret=%d\n", ret); if (WIFEXITED(status)) { printf("child exit with %d\n", WEXITSTATUS(status)); } if (WIFSIGNALED(status)) { printf("child killed by %d\n", WTERMSIG(status)); } ret = waitpid(-1, nullptr, WNOHANG); if (ret < 0) { perror("wait err"); } while (true) { sleep(1); } }
waitpid回收多个子进程(且忽略子进程状态即死亡原因)
int i { 0 }; int n { 5 }; int ret { 0 }; pid_t pid { 0 }; for (i = 0; i < n; ++i) { pid = fork(); if (pid == 0) { printf("i am child, pid=%d\n", getpid()); sleep(2); break; } } if (pid > 0) { while ((ret = waitpid(-1, nullptr, WNOHANG)) != -1) { sleep(1); printf("ret = %d\n", ret); } printf("ret = %d\n", ret); while (true) { sleep(1); printf("i am parent, self=%d, child_pid=%d\n", getpid(), pid); } }