exit()与wait-()函数

本文深入探讨了进程管理的核心概念,包括进程的终止、异常处理、父子进程的关系及如何使用wait和waitpid函数捕获子进程状态。同时介绍了exit、_Exit与_exit函数的区别,并通过实例演示了如何避免僵尸进程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这节的知识我就基本只是把书上的整理了一下, 仅仅只是个人觉得有用的整理了下。

exit()函数

  • _Exit() : 其目的是为进程提供一种无需运行终止处理程序或信号处理程序而终止的方法
  • _exit() : _exit()由exit()调用,处理UNIX特定的细节
  • 最后一个进程调用return等其他终止的返回值并不作为进程的返回值. 当最后一个进程从启动例程返回时, 该进程以终止状态0返回.
  • 终止
不管进程是怎么终止的, 最后都会调用内核的同一段代码. 这段代码会关闭所有的流操作, 释放所使用的存储器.
在异常终止时, 系统会返回一个异常终止状态.
父进程通过wait()watipid()函数来获得终止状态.
在最后调用exit()时, 转化为终止状态
在父进程比子进程先终止时,父进程会改变为init进程(也是之前说的init会成为所有孤进程的父进程(原因 :在进程终止时, 内核逐个检查所有活动的进程, 以判断是不是正要终止进程的子进程, 如果是, 则该进程的父进程ID改为1.)
由init收养的父进程是不会变为僵尸进程的(僵尸进程 : 一个已终止, 但是其父进程没有对其做善后处理)

wait*()函数

wait()

  1. pid_t wait(int *statloc)
    • 子进程运行时, wait使调用者堵塞
    • statloc != NULL 则进程的终止状态就存放在其所指的单元, statloc == NULL 则不关心其终止状态
    • 当一个子进程终止就立刻返回。

waitpid()

  1. pid_t waitpid(pid_t pid, int *statloc, int options)
    • 可以并不堵塞
    • 可以等待pid指定的进程终止在执行waitpid()的调用者。
    • statloc != NULL 则进程的终止状态就存放在其所指的单元, statloc == NULL 则不关心其终止状态
    • pid的值
      pid == -1 等待任意子进程
      pid > 0 等待 “进程ID == pid” 的进程
      pid == 0 等待 ”组ID == 调用进程组ID”的任意进程
      pid < -1 等待 “组ID == |pid|“ 的进程

如果一个进程fork一个子进程, 但是想等待子进程终止再进行, 也不希望子进程处于僵尸状态直到父进程终止, 这就需要调用两次fork() (通俗一点就是希望父进程在子进程之前终止)

  1. /*************************************************************************
  2. > File Name: fork_wait_子进程.cpp
  3. > Author: Function_Dou
  4. > Mail:
  5. > Created Time: 2018年01月30日 星期二 11时23分27秒
  6. pid_t waitpid(pid_t pid, int *statloc, int options)
  7. 可以并不堵塞
  8. 可以等待pid指定的进程终止在执行waitpid()的调用者。
  9. statloc != NULL 则进程的终止状态就存放在其所指的单元, statloc == NULL 则不关心其终止状态
  10. pid的值
  11. pid == -1 等待任意子进程
  12. pid > 0 等待 “进程ID == pid” 的进程
  13. pid == 0 等待 ”组ID == 调用进程组ID”的任意进程
  14. pid < -1 等待 “组ID == |pid|“ 的进程
  15. ************************************************************************/
  16. #include <stdio.h>
  17. #include "apue.h"
  18. #include <sys/wait.h>
  19. int main(void)
  20. {
  21. // pid_t pid = -1;
  22. pid_t pid;
  23. if((pid = fork()) < 0)
  24. err_sys("fork error");
  25. else if(pid == 0)
  26. {
  27. if((pid = fork()) < 0)
  28. err_sys("fork error");
  29. else if(pid > 0)
  30. exit(0);
  31. sleep(2);
  32. printf("second child, parent pid = %ld\n", (long)getppid());
  33. exit(0);
  34. }
  35. if(waitpid(pid, NULL, 0) != pid)
  36. err_sys("waitpid error");
  37. exit(0);
  38. }
  39. /* 样例
  40. [root@192 Process]# ./a.out
  41. [root@192 Process]# second child, parent pid = 1
  42. [root@192 Process]#
  43. */
说明
WIFEXITED正常终止(真)可执行WEXITSTATUS 来获取子进程传递给exit, _exit参数
WIFSIGNALED异常终止(真)可执行WTERMSIG 获取子进程终止的信号编号
WIFSTOPPED暂停子进程的返回的状态(真)WSTOPSIG 获取子进程暂停的信号编号
  1. /*************************************************************************
  2. > File Name: exit__退出状态.cpp
  3. > Author: Function_Dou
  4. > Mail:
  5. > Created Time: 2018年01月31日 星期三 21时40分13秒
  6. ************************************************************************/
  7. #include <stdio.h>
  8. #include "apue.h"
  9. #include <sys/wait.h>
  10. void pr_exit(int status)
  11. {
  12. // 正常终止
  13. if(WIFEXITED(status))
  14. printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
  15. // 异常终止
  16. else if(WIFSIGNALED(status))
  17. printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status),
  18. #ifdef WCOREDUMP
  19. WCOREDUMP(status) ? " (core file generated)" : "");
  20. #else
  21. "");
  22. #endif
  23. // 暂停子进程
  24. else if(WIFSTOPPED(status))
  25. printf("child stopped, signal number = %d", WSTOPSIG(status));
  26. }
  27. int main(void)
  28. {
  29. pid_t pid;
  30. int status;
  31. // 1
  32. if((pid = fork()) < 0)
  33. err_sys("fork error");
  34. else if(pid == 0)
  35. exit(7);
  36. if(wait(&status) != pid)
  37. err_sys("wait error");
  38. pr_exit(status);
  39. // 2
  40. if((pid = fork()) < 0)
  41. err_sys("fork error");
  42. else if(pid == 0)
  43. abort();
  44. if(wait(&status) != pid)
  45. err_sys("wait error");
  46. pr_exit(status);
  47. // 3
  48. if((pid = fork()) < 0)
  49. err_sys("fork error");
  50. else if(pid == 0)
  51. status /= 0;
  52. if(wait(&status) != pid)
  53. err_sys("wait error");
  54. pr_exit(status);
  55. exit(0);
  56. }
  57. /* 样例
  58. ./a.out
  59. normal termination, exit status = 7
  60. abnormal termination, signal number = 6 (core file generated)
  61. abnormal termination, signal number = 8 (core file generated)
  62. */

竞争条件

竞争条件 : 当多个进程对共享数据进行某种处理, 但是最后结果又得取决于进程运行的顺序。(通俗一点就是运行还得看系统的调度顺序)

轮询:等待父进程终止在进行子进程。(消耗cpu资源)

  1. while(getppid != 1)
  2. sleep(1);

竞争条件的程序

  1. /*************************************************************************
  2. > File Name: TELL_WAIT_竞争条件.cpp
  3. > Author: Function_Dou
  4. > Mail:
  5. > Created Time: 2018年01月31日 星期三 22时38分46秒
  6. int putc(int ch, FILE *stream);
  7. 参数ch表示要输入的位置,参数stream为要输入的流。
  8. 若正确,返回输入的的字符,否则返回EOF。
  9. ************************************************************************/
  10. #include <stdio.h>
  11. #include "apue.h"
  12. static void charatatime(char *);
  13. int main(void)
  14. {
  15. pid_t pid;
  16. // 修改方法2
  17. // TELL_WAIT();
  18. // 修改方法1
  19. // if((pid = vfork()) < 0) 没有竞争关系了, 因为先进行子进程
  20. if((pid = fork()) < 0)
  21. err_sys("fork error");
  22. else if(pid == 0)
  23. {
  24. // 修改方法2
  25. // WAIT_PARENT();
  26. charatatime("one chile\n");
  27. }
  28. else
  29. {
  30. // 修改方法2
  31. // TELL_CHILD(pid);
  32. charatatime("first parent\n");
  33. }
  34. exit(0);
  35. }
  36. static void charatatime (char *str)
  37. {
  38. char *ptr;
  39. int c;
  40. setbuf(stdout, NULL);
  41. for(ptr = str; (c = *ptr++) != NULL; )
  42. putc(c, stdout);
  43. }
  44. /* 样例
  45. [root@192 Process]# ./a.out
  46. first oparent
  47. ne chile
  48. [root@192 Process]# ./a.out
  49. first paronent
  50. e chile
  51. [root@192 Process]# ./a.out
  52. first parenot
  53. ne chile
  54. */

每次的结果不一样, 因为这个要跟此时的进程顺序有关。
书上的修改方法2运行有问题, 因为那些函数没有包含进去, 所以无法测试。

看样子导出html格式代码就都有高亮了。
### 关于 `wait` 函数及其变体的使用说明 #### 1. 基本功能描述 `wait()` 和其扩展版本(如 `waitpid`, `wait3`, `wait4`)主要用于父进程等待子进程结束并获取其退出状态。这些函数能够帮助清理僵尸进程,从而释放系统资源[^2]。 #### 2. 主要形式参数解释 - **`wait(int *status)`**: 这是最简单的形式,父进程会阻塞直到任意一个子进程终止。如果没有任何子进程存在或所有子进程仍在运行,则该调用可能永远不会返回。 - 参数 `*status`: 存储子进程退出时的状态码。可以通过宏如 `WIFEXITED(status)` 和 `WEXITSTATUS(status)` 解析正常退出的具体数值[^2]。 - **`waitpid(pid_t pid, int *status, int options)`**: - 参数 `pid`: 可以指定具体的子进程 ID (`>0`)、当前用户的任何子进程 (`-1`) 或者组内的某些成员 ( `<-1 or ==0` )。 - 参数 `options`: 提供额外的行为控制选项,例如 `WNOHANG`(非阻塞模式) 和 `WUNTRACED`(捕获停止而非仅限退出)[^2]. - **`wait3(int *status, int options, struct rusage *rusage)`** 和 **`wait4(pid_t pid, int *status, int options, struct rusage *rusage)`**: - 它们提供了更详细的统计信息给可选结构体变量 `rusage` 中填充实际使用的 CPU 时间和其他资源消耗数据[^1]. #### 3. 返回值分析 成功情况下,以上各方法均返回所关注到的那个已完成生命周期转换(死亡) 的子进程 PID;失败则返回 `-1` 并设置 errno 表明原因[^2]. #### 4. 实际应用案例 假设我们创建了一个新进程来完成一项耗时任务,那么在其完成后我们需要知道结果如何以及避免成为孤儿进程: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main(){ pid_t child_pid; printf("Parent process starting...\n"); if((child_pid = fork()) == 0){ /* Child Process */ puts("Child is doing work..."); sleep(2); exit(EXIT_SUCCESS); // Simulate some job done. } else{ /* Parent waits for completion of any children processes */ int stat_val; pid_t returned_pid; returned_pid = wait(&stat_val); if(WIFEXITED(stat_val)){ printf("Process %d terminated with code %d.\n",returned_pid,WEXITSTATUS(stat_val)); } return EXIT_SUCCESS; } } ``` #### 5. 错误排查指南 - 如果发现程序卡住可能是由于未正确处理 SIGCHLD 导致无法触发相应的回收逻辑. - 当多次连续启动大量短期存活的小型服务端实例时容易产生过多僵尸记录占用内存空间——建议采用循环配合 `while(waitpid(-1,&st,WNOHANG)>0){}` 方式逐一清除它们. --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值