赞
踩
fork创建了子进程,子进程帮父进程完成某种任务后,父进程需要用 wait或者waitpid等待子进程的退出。
那为什么要进程等待?
1、通过获取子进程退出的消息,父进程可以得知子进程的执行结果。
2、进程等待可以保证子进程先退出,父进程后退出。
3、子进程退出的时候会先进入僵尸状态,进程一旦变成僵尸状态,连kill -9
也没办法杀死,因为没办法杀死一个已经死去的进程。这时候会有内存泄漏的问题,就需要父进程等待,来释放子进程占用的资源。
在Linux下可以用man 2 wait
查看:
wait
方法:
- #include <sys/types.h>
- #include <sys/wait.h>
-
- pid_t wait(int *status);
返回值:
成功时返回等待进程的pid,失败返回-1。
参数:
输出型参数,获取子进程的退出状态,不关心则设置成为NULL
测试:
- [zjy@VM-16-3-centos lessoncode]$ cat test.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
-
- int main()
- {
- pid_t id = fork();
- if(id == 0)
- {
- // child
- int cnt = 5;
- while(cnt)
- {
- printf("Child is running: cnt=%d\n", cnt);
- --cnt;
- sleep(1);
- }
- exit(0);
- }
- sleep(10);
- printf("father wait begin\n");
- // father
- pid_t ret = wait(NULL);
- if(ret > 0)
- {
- printf("father wait: %d, success\n", ret);
- }
- else{
- printf("father wait failed\n");
- }
-
- sleep(10);
- return 0;
- }
运行结果:
- [zjy@VM-16-3-centos lessoncode]$ while :; do ps axj | head -1 && ps axj | grep test| grep -v grep; sleep 1; echo "#######################################"; done
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
- #######################################
子进程的状态从睡眠状态,变成僵尸进程,最后被父进程回收。
pid_t waitpid(pid_t pid, int *status, int options);
返回值:
1、正常返回:waitpid返回收集到的子进程的进程pid
2、如果options设置成 WNOHANG
选项,表示非阻塞等待,调用时waitpid发现自己没有退出的子进程可以收集,返回0
3、如果调用出错,则返回-1,这时errno会被设置成相应的值,指示错误所在。
参数:pid
:
pid=-1时,表示任一子进程,等同于wait
pid>0,等待id和pid相等的子进程。
status
:
WIFEXITED(status):查看进程是否正常退出,正常退出-真
WEXITSTATUS(status):查看进程的退出码,WIFEXITED非零,提取子进程的退出码。
options
WNOHANG:如果pid的子进程没有结束,那么waitpid返回值是0,不予以等待,如果正常结束,返回该子进程的id。
0:表示非阻塞等待
status是输出型参数,父进程最终拿到的status结果,和子进程如何退出是强相关的。(子进程有3中进程退出的情况,可以看上一篇博客)
那可以联系一个知识点,bash是命令行启动所有进程的父进程,bash用wait的方法得到子进程的退出结果,所以我们可以用echo $?
查到子进程的退出码。
wait和waitpid,都有status参数,该参数是输出型参数,由OS填充。
如果传递NULL,表示不管子进程的退出状态信息。
status是int类型,但是实际上,32个比特位只使用16个比特位(低16位)
所以可以用位运算得到退出码和终止信号:
- exit code: (status>>8)&0xFF
- signal code: status & 0x7F
不用位操作,可以使用宏来解决:
WIFEXITED(status)
和WEXITSTATUS(status)
WIFEXITED(status):查看进程是否正常退出,正常退出-真
WEXITSTATUS(status):查看进程的退出码,WIFEXITED非零,提取子进程的退出码。
waitpid函数的第三个参数 options,0是默认行为,表示阻塞等待,WNOHANG表示非阻塞等待。
阻塞等待,表示一直干等着,等的时候什么事情都不干;非阻塞等待每隔一段时间等待,她没好,过几分钟再等待。(比如打电话这个例子)非阻塞等待可能需要多次检测,这是基于 非阻塞等待的轮循方案。
父进程在子进程退出后继续程序,如下图:
ALL IN ALL:
阻塞等待和非阻塞等待都是等待的方式,都是父进程等待子进程,等待子进程退出。
QUESTION1:
阻塞是不是意味着父进程不被调度执行了呢?
父进程等待的本质是,把父进程的PCB放进等待队列,进程状态从R变成S,在等待队列等待时,不被调度执行。
所以,
==阻塞的本质:==进程的PCB放进等待队列,并将进程的状态R改成S状态。
返回的本质:进程的PCB从等待队列放进运行队列,从而被CPU调用。
所以当我们看到某些应用或者操作系统本身,卡住了或者长时间不动,应用或者程序hang住了!
WNOHANG情况下,waitpid的返回值:
1、子进程没有退出,返回值=0
2、子进程退出,waitpid调用成功,返回>0,失败则返回-1 .
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/wait.h>
-
- int main()
- {
- pid_t pid = fork();
-
- if(pid < 0)
- {
- printf("%s fork error\n", __FUNCTION__);
- return 1;
- }
- else if(pid == 0) { // child
- printf("child is run, pid is: %d\n", getpid());
- int cnt = 5;
- while(cnt)
- {
- printf("child[%d] is running, cnt = %d\n", getpid(), cnt);
- cnt--;
- sleep(1);
- }
- exit(1);
- } else {
- // father
- int status = 0;
- pid_t ret = 0;
-
- do {
- ret = waitpid(-1, &status,WNOHANG); // 非阻塞式等待
- if(ret == 0) {
- // 子进程还没结束
- printf("Do father things\n");
- }
- sleep(1);
- }while(ret == 0);
-
- if(WIFEXITED(status) &&ret == pid)
- {
- printf("wait child success, child return code is :%d\n", WEXITSTATUS(status));
- }
- else
- {
- printf("wait child failed, return.\n");
- return 1;
- }
- }
- return 0;
- }
运行结果:
- [zjy@VM-16-3-centos lessoncode]$ ./test
- Do father things
- child is run, pid is: 22362
- child[22362] is running, cnt = 5
- Do father things
- child[22362] is running, cnt = 4
- Do father things
- child[22362] is running, cnt = 3
- Do father things
- child[22362] is running, cnt = 2
- Do father things
- child[22362] is running, cnt = 1
- Do father things
- wait child success, child return code is :1
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/wait.h>
-
- int main()
- {
- pid_t pid = fork();
-
- if(pid < 0) {
- printf("%s fork error\n", __FUNCTION__);
- return 1;
- }
- else if(pid == 0) { // child
- printf("child is run, pid is: %d\n", getpid());
- int cnt = 5;
- while(cnt)
- {
- printf("child[%d] is running, cnt = %d\n", getpid(), cnt);
- cnt--;
- sleep(1);
- }
- exit(1);
- } else {
- int status = 0;
- pid_t ret = waitpid(-1, &status, 0); // 阻塞式等待
- printf("wait test...\n");
- if(WIFEXITED(status) && ret == pid)
- {
- printf("wait child success, child return code is :%d\n", WEXITSTATUS(status));
- }
- else {
- printf("wait child failed, return.\n");
- return 1;
- }
-
- }
-
- return 0;
- }
运行结果:
- child is run, pid is: 25943
- child[25943] is running, cnt = 5
- child[25943] is running, cnt = 4
- child[25943] is running, cnt = 3
- child[25943] is running, cnt = 2
- child[25943] is running, cnt = 1
- wait test...
- wait child success, child return code is :1
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。