当前位置:   article > 正文

Linux--进程等待wait/waitpid && status详解 && (非)阻塞等待(代码)(转载)_waitpid status

waitpid status

文章目录

进程等待原因

fork创建了子进程,子进程帮父进程完成某种任务后,父进程需要用 wait或者waitpid等待子进程的退出。

那为什么要进程等待?
1、通过获取子进程退出的消息,父进程可以得知子进程的执行结果。
2、进程等待可以保证子进程先退出,父进程后退出。
3、子进程退出的时候会先进入僵尸状态,进程一旦变成僵尸状态,连kill -9也没办法杀死,因为没办法杀死一个已经死去的进程。这时候会有内存泄漏的问题,就需要父进程等待,来释放子进程占用的资源。

进程等待方法

wait

在Linux下可以用man 2 wait查看:
在这里插入图片描述
在这里插入图片描述

wait方法:

  1. #include <sys/types.h>
  2. #include <sys/wait.h>
  3. pid_t wait(int *status);

返回值:
成功时返回等待进程的pid,失败返回-1。

参数:
输出型参数,获取子进程的退出状态,不关心则设置成为NULL

测试:

  1. [zjy@VM-16-3-centos lessoncode]$ cat test.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <sys/wait.h>
  6. #include <unistd.h>
  7. int main()
  8. {
  9. pid_t id = fork();
  10. if(id == 0)
  11. {
  12. // child
  13. int cnt = 5;
  14. while(cnt)
  15. {
  16. printf("Child is running: cnt=%d\n", cnt);
  17. --cnt;
  18. sleep(1);
  19. }
  20. exit(0);
  21. }
  22. sleep(10);
  23. printf("father wait begin\n");
  24. // father
  25. pid_t ret = wait(NULL);
  26. if(ret > 0)
  27. {
  28. printf("father wait: %d, success\n", ret);
  29. }
  30. else{
  31. printf("father wait failed\n");
  32. }
  33. sleep(10);
  34. return 0;
  35. }

运行结果:

  1. [zjy@VM-16-3-centos lessoncode]$ while :; do ps axj | head -1 && ps axj | grep test| grep -v grep; sleep 1; echo "#######################################"; done
  2. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  3. #######################################
  4. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  5. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  6. 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  7. #######################################
  8. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  9. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  10. 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  11. #######################################
  12. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  13. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  14. 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  15. #######################################
  16. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  17. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  18. 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  19. #######################################
  20. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  21. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  22. 29275 29276 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  23. #######################################
  24. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  25. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  26. 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
  27. #######################################
  28. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  29. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  30. 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
  31. #######################################
  32. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  33. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  34. 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
  35. #######################################
  36. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  37. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  38. 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
  39. #######################################
  40. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  41. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  42. 29275 29276 29275 16230 pts/0 29275 Z+ 1001 0:00 [test] <defunct>
  43. #######################################
  44. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  45. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  46. #######################################
  47. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  48. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  49. #######################################
  50. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  51. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  52. #######################################
  53. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  54. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  55. #######################################
  56. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  57. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  58. #######################################
  59. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  60. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  61. #######################################
  62. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  63. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  64. #######################################
  65. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  66. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  67. #######################################
  68. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  69. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  70. #######################################
  71. PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  72. 16230 29275 29275 16230 pts/0 29275 S+ 1001 0:00 ./test
  73. #######################################

子进程的状态从睡眠状态,变成僵尸进程,最后被父进程回收。

waitpid

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是输出型参数,父进程最终拿到的status结果,和子进程如何退出是强相关的。(子进程有3中进程退出的情况,可以看上一篇博客)

那可以联系一个知识点,bash是命令行启动所有进程的父进程,bash用wait的方法得到子进程的退出结果,所以我们可以用echo $?查到子进程的退出码。

wait和waitpid,都有status参数,该参数是输出型参数,由OS填充。

如果传递NULL,表示不管子进程的退出状态信息。

status是int类型,但是实际上,32个比特位只使用16个比特位(低16位)

在这里插入图片描述

在这里插入图片描述
所以可以用位运算得到退出码和终止信号:

  1. exit code: (status>>8)&0xFF
  2. signal code: status & 0x7F

不用位操作,可以使用宏来解决:

WIFEXITED(status)WEXITSTATUS(status)

WIFEXITED(status):查看进程是否正常退出,正常退出-真
WEXITSTATUS(status):查看进程的退出码,WIFEXITED非零,提取子进程的退出码。

(非)阻塞等待

waitpid函数的第三个参数 options,0是默认行为,表示阻塞等待,WNOHANG表示非阻塞等待。

阻塞等待,表示一直干等着,等的时候什么事情都不干;非阻塞等待每隔一段时间等待,她没好,过几分钟再等待。(比如打电话这个例子)非阻塞等待可能需要多次检测,这是基于 非阻塞等待的轮循方案

  • 如果子进程已经退出,调用wait/waitpid会立即返回,并释放资源,获得子进程退出信息。
  • 如果在任意是时刻都调用wait/waitpid,子进程存在且正常运行,进程可能阻塞。
  • 如果不存在该子进程,则立即出错返回。

父进程在子进程退出后继续程序,如下图:

在这里插入图片描述

ALL IN ALL:
阻塞等待和非阻塞等待都是等待的方式,都是父进程等待子进程,等待子进程退出

QUESTION1:
阻塞是不是意味着父进程不被调度执行了呢?
父进程等待的本质是,把父进程的PCB放进等待队列,进程状态从R变成S,在等待队列等待时,不被调度执行。

所以,

==阻塞的本质:==进程的PCB放进等待队列,并将进程的状态R改成S状态。

返回的本质:进程的PCB从等待队列放进运行队列,从而被CPU调用。

所以当我们看到某些应用或者操作系统本身,卡住了或者长时间不动,应用或者程序hang住了!

WNOHANG情况下,waitpid的返回值:
1、子进程没有退出,返回值=0
2、子进程退出,waitpid调用成功,返回>0,失败则返回-1 .
在这里插入图片描述

进程的非阻塞等待方式代码

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/wait.h>
  5. int main()
  6. {
  7. pid_t pid = fork();
  8. if(pid < 0)
  9. {
  10. printf("%s fork error\n", __FUNCTION__);
  11. return 1;
  12. }
  13. else if(pid == 0) { // child
  14. printf("child is run, pid is: %d\n", getpid());
  15. int cnt = 5;
  16. while(cnt)
  17. {
  18. printf("child[%d] is running, cnt = %d\n", getpid(), cnt);
  19. cnt--;
  20. sleep(1);
  21. }
  22. exit(1);
  23. } else {
  24. // father
  25. int status = 0;
  26. pid_t ret = 0;
  27. do {
  28. ret = waitpid(-1, &status,WNOHANG); // 非阻塞式等待
  29. if(ret == 0) {
  30. // 子进程还没结束
  31. printf("Do father things\n");
  32. }
  33. sleep(1);
  34. }while(ret == 0);
  35. if(WIFEXITED(status) &&ret == pid)
  36. {
  37. printf("wait child success, child return code is :%d\n", WEXITSTATUS(status));
  38. }
  39. else
  40. {
  41. printf("wait child failed, return.\n");
  42. return 1;
  43. }
  44. }
  45. return 0;
  46. }

运行结果:

  1. [zjy@VM-16-3-centos lessoncode]$ ./test
  2. Do father things
  3. child is run, pid is: 22362
  4. child[22362] is running, cnt = 5
  5. Do father things
  6. child[22362] is running, cnt = 4
  7. Do father things
  8. child[22362] is running, cnt = 3
  9. Do father things
  10. child[22362] is running, cnt = 2
  11. Do father things
  12. child[22362] is running, cnt = 1
  13. Do father things
  14. wait child success, child return code is :1

进程的阻塞等待方式代码

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/wait.h>
  5. int main()
  6. {
  7. pid_t pid = fork();
  8. if(pid < 0) {
  9. printf("%s fork error\n", __FUNCTION__);
  10. return 1;
  11. }
  12. else if(pid == 0) { // child
  13. printf("child is run, pid is: %d\n", getpid());
  14. int cnt = 5;
  15. while(cnt)
  16. {
  17. printf("child[%d] is running, cnt = %d\n", getpid(), cnt);
  18. cnt--;
  19. sleep(1);
  20. }
  21. exit(1);
  22. } else {
  23. int status = 0;
  24. pid_t ret = waitpid(-1, &status, 0); // 阻塞式等待
  25. printf("wait test...\n");
  26. if(WIFEXITED(status) && ret == pid)
  27. {
  28. printf("wait child success, child return code is :%d\n", WEXITSTATUS(status));
  29. }
  30. else {
  31. printf("wait child failed, return.\n");
  32. return 1;
  33. }
  34. }
  35. return 0;
  36. }

运行结果:

  1. child is run, pid is: 25943
  2. child[25943] is running, cnt = 5
  3. child[25943] is running, cnt = 4
  4. child[25943] is running, cnt = 3
  5. child[25943] is running, cnt = 2
  6. child[25943] is running, cnt = 1
  7. wait test...
  8. wait child success, child return code is :1
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/933721
推荐阅读
相关标签
  

闽ICP备14008679号