当前位置:   article > 正文

linux18:进程等待

linux18:进程等待

进程等待的必要性

1:子进程创建的目的是要完成父进程指派的某个任务,当子进程运行完毕退出时,父进程需要通过进程等待的方式,回收子进程资源,获取子进程退出信息(子进程有无异常?没有异常结果是否正确?检查退出码查看错误原因)
2:父进程如果一直不回收,就可能造成子进程‘僵尸进程’的问题,子进程一直得不到父进程的回收,进而造成内存泄漏。
3:子进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力杀死一个已经死去的进程。只能通过进程等待将他进行回收

子进程等待的两个方法  wait  与  waitpid

1:wait方法:

wait 函数用于等待一个已启动的子进程结束。这个函数通常与 fork 函数一起使用,fork 用于创建子进程。wait 函数会挂起(父进程阻塞等待)调用它的父进程,直到有一个子进程结束或到达指定的时间限制。

头文件

  1. #include <sys/types.h>
  2. #include <sys/wait.h>

函数原型:

pid_t wait(int *status);

status:输出型参数,一个指向整数的指针,如果提供一个变量,子进程的结束状态将被存储在所指向的整数变量

 子进程退出结束状态的三种情况:1:代码运行完毕,结果正确

                                                       2:代码运行完毕,结果不正确

                                                       3:异常终止

        退出码   (status>>8)&0xFF     终止信息    status&0x7F          

      返回值:

  • 成功时,wait 返回结束的子进程的进程ID。
  • 如果有错误发生,返回 -1,并设置 errno 以指示错误类型。

例子:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/wait.h>
  5. #include <unistd.h>
  6. int main()
  7. {
  8. pid_t pid;
  9. int status;
  10. pid = fork(); // 创建子进程
  11. if (pid == -1) // 返回值是1时,说明fork() 调用失败
  12. {
  13. perror("fork");
  14. exit(EXIT_FAILURE);
  15. }
  16. else if (pid == 0) // 子进程
  17. {
  18. printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
  19. exit(EXIT_SUCCESS);
  20. }
  21. else // 父进程
  22. {
  23. printf("Parent process, PID: %d, PPID: %d\n", getpid(), getppid());
  24. pid = wait(&status); // 挂起直到子进程结束
  25. if (pid == -1) // 返回值是1时,说明wait() 调用失败
  26. {
  27. perror("wait");
  28. exit(EXIT_FAILURE);
  29. }
  30. if (WIFEXITED(status)) // 最后查看检查子进程的结束状态
  31. {
  32. printf("Child process exited with status %d\n", WEXITSTATUS(status));
  33. }
  34. }
  35. return 0;
  36. }

这个例子中,父进程使用 fork 创建了一个子进程。

子进程打印出它的进程ID和父进程ID,然后退出。

父进程则调用 wait 函数并传入一个指向整数的指针,这个整数将存储子进程的结束状态。

因为有wait方法存在,一旦子进程结束,父进程将继续执行,并检查子进程的结束状态。

2:waitpid方法:

wait 函数不同,waitpid 允许父进程指定要等待的子进程的进程标识符(PID),这提供了更细粒度的控制。

waitpid 对于处理多个子进程的情况特别有用,因为它允许父进程等待特定的子进程或一组子进程。

头文件与wait文件相同

  1. #include <sys/types.h>
  2. #include <sys/wait.h>

函数原型:

pid_t waitpid(pid_t pid, int *status, int options);
  • pid_t pid:指定要等待的子进程的 PID。
  • 如果 pid > 0,waitpid 等待特定的pid子进程。
  • 如果 pid == -1waitpid 等待任何子进程,类似于 wait 函数。
  • 如果 pid < -1,waitpid 等待进程组 ID(PGID)等于 pid 绝对值的任何子进程。
  • ---------------------------------------------------------------------------------------------------------------------------

  • int *status:一个指向整数的指针,如果提供一个变量,结束状态将被存储在所指向的整数变量中
  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
  • ---------------------------------------------------------------------------------------------------------------------------
  • int options:指定 waitpid 调用的行为的选项,
  •  WNOHANG(非阻塞轮询,使等待非阻塞)
    pid 指定的子进程没有结束,则 waitpid() 函数返回 0 ,不予以等待。若正常结束,则返回该子进程的ID
  •  WUNTRACED(除了已结束的子进程,还报告已停止的子进程)。
  •  0 表示默认选项

  • ---------------------------------------------------------------------------------------------------------------------------

      返回值与wait一样:

  • 成功时,wait 返回结束的子进程的进程ID。
  • 如果有错误发生,返回 -1,并设置 errno 以指示错误类型。

例子:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/wait.h>
  5. #include <unistd.h>
  6. int main()
  7. {
  8. pid_t pid, wpid;
  9. int status;
  10. // 创建子进程
  11. pid = fork();
  12. if (pid == -1)
  13. {
  14. perror("fork");
  15. exit(EXIT_FAILURE);
  16. }
  17. if (pid == 0) // 子进程
  18. {
  19. printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
  20. sleep(5);
  21. printf("Child process terminating\n");
  22. exit(EXIT_SUCCESS);
  23. }
  24. // waitpid方法,父进程等待特定的子进程结束
  25. wpid = waitpid(pid, &status, 0);
  26. if (wpid == -1)
  27. {
  28. perror("waitpid");
  29. exit(EXIT_FAILURE);
  30. }
  31. printf("Child process exited with status %d\n", WEXITSTATUS(status));
  32. return 0;
  33. }

在这个示例中,父进程创建了一个子进程,

一旦sleep(5);5s过后,子进程结束,waitpid 返回子进程的 PID,父进程可以检查子进程的退出状态。

阻塞轮询例子

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <sys/wait.h>
  4. #include <stdlib.h>
  5. int main()
  6. {
  7. pid_t pid;
  8. int status;
  9. pid = fork(); // 创建子进程
  10. if (pid < 0) // fork 出错
  11. {
  12. perror("fork failed");
  13. exit(EXIT_FAILURE);
  14. }
  15. else if (pid == 0) // 子进程
  16. {
  17. printf("Child process, PID: %d\n", getpid());
  18. sleep(5);
  19. exit(0);
  20. }
  21. else // 父进程
  22. {
  23. while (1)
  24. {
  25. pid_t ret = waitpid(pid, &status, WNOHANG); // 非阻塞等待
  26. if (ret == 0) // 子进程未结束,继续轮询
  27. {
  28. printf("Child process is still running.\n");
  29. sleep(1); // 等待1秒后再次检查
  30. }
  31. else if (ret == pid) // 子进程已结束
  32. {
  33. if (WIFEXITED(status))
  34. {
  35. printf("Child process exited with status %d\n", WEXITSTATUS(status)); //提取子进程退出码
  36. }
  37. else if (WIFSIGNALED(status))
  38. {
  39. printf("Child process terminated by signal %d\n", WTERMSIG(status)); //终止子进程信号
  40. }
  41. break;
  42. }
  43. else // waitpid 出错
  44. {
  45. perror("waitpid failed");
  46. exit(EXIT_FAILURE);
  47. }
  48. }
  49. }
  50. return 0;
  51. }

多个进程等待

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/wait.h>
  5. #include <unistd.h>
  6. //0--5s 子进程运行,父进程运行
  7. //5--15s 子进程回收,父进程运行
  8. int main()
  9. {
  10. pid_t child_pid;
  11. int status;
  12. int i;
  13. for (i = 0; i < 5; ++i) // 创建5个子进程
  14. {
  15. child_pid = fork();
  16. if (child_pid == -1) // fork() 调用失败
  17. {
  18. perror("fork");
  19. exit(EXIT_FAILURE);
  20. }
  21. if (child_pid == 0) // 这是子进程,每个子进程运行5s
  22. {
  23. int n = 5;
  24. while (n--)
  25. {
  26. printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
  27. sleep(1);
  28. }
  29. exit(EXIT_SUCCESS);
  30. }
  31. }
  32. for (i = 0; i < 5; ++i)
  33. {
  34. if (wait(&status) == -1) //循环5次wait来等待创建的5个子进程
  35. {
  36. perror("wait");
  37. exit(EXIT_FAILURE);
  38. }
  39. // 检查子进程的结束状态
  40. if (WIFEXITED(status))
  41. {
  42. printf("Child process exited with status %d\n", WEXITSTATUS(status));
  43. }
  44. }
  45. sleep(10); //父进程等待10s
  46. return 0;
  47. }

我们首先通过一个 for 循环调用 fork() 五次,创建五个子进程。

每个子进程都会打印出自己的 PID 和 PPID,然后调用 sleep(1) 以模拟执行任务,接着通过 exit() 函数正常退出。

父进程在创建了所有子进程之后,会进入另一个 for 循环,使用 wait() 函数等待每个子进程结束。

wait() 会挂起父进程,直到有一个子进程结束。一旦有子进程结束,wait() 返回,父进程会检查子进程的结束状态,如果子进程是正常结束的,会打印出子进程的退出状态码。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/462499
推荐阅读
相关标签
  

闽ICP备14008679号