【Linux探索学习】第十八弹——进程等候:深入剖析操纵系统中的进程等候机 ...

打印 上一主题 下一主题

主题 801|帖子 801|积分 2403

Linux学习笔记:https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482
前言:
   在Linux操纵系统中,进程是资源的管理和实行单元,每个进程都有其自己的生命周期。在进程的实行过程中,进程可能需要等候一些资源或事件的发生,例如等候I/O操纵完成、等候信号、等候其他进程的结束等,这些都叫做进程等候。这些我们在前面讲进程状态的时候基本上都提到过,今天我们重点来讲解父进程等候子进程这类等候别的进程结束的问题
  目次
1. 父进程为什么要等候子进程
2. 父进程等候子进程的常用函数
3. wait() 和 waitpid() 函数详解
3.1 wait()
3.2 waitpid()
4. 使用 SIGCHLD 信号等候子进程
5. 僵尸进程与制止方法
6. 总结


1. 父进程为什么要等候子进程

在前面上篇我们已经讲过僵尸状态的问题,子进程在实行结束后,如果父进程不及时进行接受处理,子进程就会进入僵尸状态,进入僵尸状态后,从而造成内存泄漏,而且kill -9信号也不能进行处理,同时我们的父进程也需要通过进程退出的方式来回收子进程资源,获取子进程退出信息,以是说进程等候十分有必要。

2. 父进程等候子进程的常用函数

Linux 提供了多个函数用于父进程等候子进程的结束:
函数名形貌wait()阻塞父进程,直到任一子进程退出,返回退出的子进程 PID。waitpid()更灵活的等候函数,可选择等候特定子进程,支持非阻塞模式。waitid()雷同于 waitpid(),但功能更强大,支持更具体的选项。signal() 和 sigaction()注册 SIGCHLD 信号处理函数,用于非阻塞地获取子进程状态。 这四个函数中我们重要用到的是前两个函数,以是我们下面对前两个函数进行具体讲解

3. wait() 和 waitpid() 函数详解

3.1 wait()

wait() 是最简单的等候子进程的函数,用法如下:
  1. #include <sys/types.h>
  2. #include <sys/wait.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. int main() {
  7.     pid_t pid = fork();
  8.     if (pid == 0) {
  9.         // 子进程
  10.         printf("Child process running...\n");
  11.         sleep(3);  // 模拟一些操作
  12.         printf("Child process exiting...\n");
  13.         exit(42);  // 退出码为 42
  14.     } else {
  15.         // 父进程
  16.         int status;
  17.         pid_t child_pid = wait(&status);  // 等待任意子进程结束
  18.         if (WIFEXITED(status)) {  // 检查子进程是否正常退出
  19.             printf("Child process %d exited with status %d\n", child_pid, WEXITSTATUS(status));
  20.         }
  21.     }
  22.     return 0;
  23. }
复制代码
输出示例:
  1. Child process running...
  2. Child process exiting...
  3. Child process 12345 exited with status 42
复制代码

分析:


  • wait(&status) 阻塞父进程,直到有子进程退出。
  • 使用宏 WIFEXITED 和 WEXITSTATUS 分别检查子进程是否正常退出及其退出码。
补充:


  • 上面的例子中子进程只有一个,但有些时候我们可能有多个子进程,这个时候系统接纳的是循环等候的方法来回收每一个子进程
  • 父进程在回收子进程时是随机的,也就是说当我们有多个子进程实行结束的时候,父进程先回收哪个子进程并不是确定的,是随机的,这也就是我们上面接纳循环等候的原因
  • 循环等候的具体方法会在文章末了面的总结图里面给出示例

3.2 waitpid()

waitpid() 是更灵活的等候函数,支持:
   

  • 等候特定的子进程。
  • 非阻塞模式。
  函数原型:
  1. pid_t waitpid(pid_t pid, int *status, int options);
复制代码
参数形貌pid 指定子进程的 PID,若为 -1 等候恣意子进程,与pid等效;若大于1则等候其进程ID与pid雷同的子进程
status存储子进程的退出状态。options控制等候行为(如 WNOHANG 表示非阻塞)。 我们先对上面的表格做一个小补充:
参数option作用是控制等候行为,常见的等候方式重要有两种:阻塞等候和非阻塞等候
   阻塞等候的意思就是在我们父进程等候子进程的过程中会进入阻塞状态,不会做别的的事情,直到子进程运行结束后再继承,而非阻塞等候则是差别的方式,非阻塞状态的父进程会在运行的过程中不停扣问查看子进程的运行环境,当子进程运行结束时,会将结果反馈给父进程,但是在这个过程中父进程并不会停下来,它还会继承自己的实行
  示例代码:
  1. #include <sys/types.h>
  2. #include <sys/wait.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. int main() {
  7.     pid_t pid1 = fork();
  8.     if (pid1 == 0) {
  9.         // 第一个子进程
  10.         printf("Child 1 running...\n");
  11.         sleep(2);
  12.         printf("Child 1 exiting...\n");
  13.         exit(1);
  14.     }
  15.     pid_t pid2 = fork();
  16.     if (pid2 == 0) {
  17.         // 第二个子进程
  18.         printf("Child 2 running...\n");
  19.         sleep(4);
  20.         printf("Child 2 exiting...\n");
  21.         exit(2);
  22.     }
  23.     // 父进程
  24.     int status;
  25.     pid_t child_pid;
  26.     while ((child_pid = waitpid(-1, &status, 0)) > 0) {  // 等待所有子进程
  27.         if (WIFEXITED(status)) {
  28.             printf("Child %d exited with status %d\n", child_pid, WEXITSTATUS(status));
  29.         }
  30.     }
  31.     return 0;
  32. }
复制代码
输出示例:
  1. Child 1 running...
  2. Child 2 running...
  3. Child 1 exiting...
  4. Child 12345 exited with status 1
  5. Child 2 exiting...
  6. Child 12346 exited with status 2
复制代码


4. 使用 SIGCHLD 信号等候子进程

信号的知识我们在前面还没进行讲解,这里还是相识为主,感爱好的可以看看,不懂的地方可以去搜一下:
SIGCHLD 信号在子进程状态发生厘革时(如退出)发送给父进程。父进程可以注册信号处理函数来处理此信号。
代码示例:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <sys/wait.h>
  6. void sigchld_handler(int sig) {
  7.     int status;
  8.     pid_t pid = wait(&status);  // 获取退出的子进程信息
  9.     if (pid > 0 && WIFEXITED(status)) {
  10.         printf("Child %d exited with status %d\n", pid, WEXITSTATUS(status));
  11.     }
  12. }
  13. int main() {
  14.     signal(SIGCHLD, sigchld_handler);  // 注册信号处理器
  15.     pid_t pid = fork();
  16.     if (pid == 0) {
  17.         // 子进程
  18.         printf("Child process running...\n");
  19.         sleep(3);
  20.         printf("Child process exiting...\n");
  21.         exit(42);
  22.     }
  23.     // 父进程
  24.     printf("Parent process doing other work...\n");
  25.     while (1) {  // 模拟父进程的其他工作
  26.         sleep(1);
  27.     }
  28.     return 0;
  29. }
复制代码
输出示例:
  1. Parent process doing other work...
  2. Child process running...
  3. Child process exiting...
  4. Child 12345 exited with status 42
复制代码


5. 僵尸进程与制止方法

文章开头我们就已经讲过僵尸进程了,通过上面对进程等候的学习,再来看一下僵尸进程的概念,看看能不能加深明白
僵尸进程(Zombie Process) 是指子进程退出后,其退出信息尚未被父进程读取的状态。虽然僵尸进程不会占用CPU,但其占用的进程表项资源有限。
制止僵尸进程的方法:
   

  • 确保父进程读取子进程状态:使用 wait() 或 waitpid()。
  • 忽略 SIGCHLD 信号:通过 signal(SIGCHLD, SIG_IGN) 忽略信号。
  • 使用守护进程(init 进程回收子进程):如果父进程终止,init 进程会自动接受并回收子进程。
  
6. 总结

父进程等候子进程是进程管理中的关键机制。在实际应用中:
   

  • 简单的任务可以使用 wait()。
  • 更复杂的需求(如非阻塞、多子进程等候)保举使用 waitpid()。
  • 及时应用可以团结 SIGCHLD 信号处理。
  合理地使用这些机制,不仅可以有效管理资源,还能制止僵尸进程的问题,提升程序的结实性和运行效率。
本篇笔记:


   感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

兜兜零元

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表