5 函数exit

exit函数、_exit函数和_Exit函数。

在7.3节中介绍了5中进程的正常终止和3种异常终止的方法。 但是无论进程如何终止,最后都会执行内核中的同一段代码。折断代码为相应进程关闭所有打开描述符,释放它所使用的存储器。 并且对于任意一种终止情形,我们都希望终止进程能通知其父进程它是如何终止的。 对于三个终止函数(exit、_exit和_Exit),实现将终止进程通知其父类的方法是将其退出状态作为参数传送给函数,然后该终止进程的父进程调用wait和waitpid函数取得其终止状态

注意

退出状态不等于终止状态 退出状态:是一个参数,是传递给三个终止函数的参数,或者main的返回值 终止状态:是一个状态,表明进程已经结束。在最后调用_exit时,内核将退出状态转换为终止状态

几种场景

  1. 子进程是父进程调用fork后生成的,子进程还会将其终止状态返回给父进程。但是如果父进程在子进程之前终止,又将如何? 答案: 所有父进程终止的子进程读改变为init(PID=1)的子进程,自身变成孤儿进程,被init进程收养。 收养过程: 当一个进程终止时,内核逐个检查所有活动进程,以判断它是否是正要终止进程的子进程,如果是,则该进程的父进程ID就更改为1.
  2. 如果子进程在父进程之前小时,那么父进程又如何能在做相应检查时得到子进程的终止状态呢? 答案: 如果子进程完全小时了额,那么父进程是无法立即知道的,内核会为每个终止进程保存了一定量的信息(例如CPU时间总量等),当终止进程的父进程调用时会得到。这种终止的进程称为僵死进程
  3. 一个由init进程收养的进程终止时会发生什么? 答案: init被编写为无论何时只要有一个子进程终止,init就会调用一个wait函数取得终止状态,这样也就放置了系统塞满僵死进程。

函数wait和waitpid

当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。父进程此时可以选择忽略该信号或者调用wait和waitpid来处理这个进程。 调用wait和waitpid所做的事情: 1. 如果其所有子进程都还在运行,则阻塞 2. 如果一个子进程已终止,整等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。 3. 如果它没有收到任何子进程,则立即出错返回

注意: 如果接收到信号立即调用wait,那么一定会有返回值,如果在随机时间点调用,则进程可能会阻塞。

wait和waitpid

include <sys/wait.h>pid_t wait(int *stattloc);pid_t waitpid(pid_t pid, int *staloc, int options);

两个函数的区别:

  1. 一个子进程终止前,wait使其调用者阻塞,而waitpid有一选项,可使调用者不阻塞。
  2. waitpid并不等待在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程。

其中staloc是一个指向终止状态的指针,若不需要终止状态则可以设置为空。

两个函数的返回值

实例: 在这里定义了一个pr_exit函数,使用上图来打印进程终止状态(以后会多次使用)

#include "apue.h"
#include <sys/wait.h>void
pr_exit(int status)
{if (WIFEXITED(status))printf("normal termination, exit status = %dn",WEXITSTATUS(status));else if (WIFSIGNALED(status))printf("abnormal termination, signal number = %d%sn",WTERMSIG(status),
#ifdef  WCOREDUMPWCOREDUMP(status) ? " (core file generated)" : "");
#else"");
#endifelse if (WIFSTOPPED(status))printf("child stopped, signal number = %dn",WSTOPSIG(status));
}

可以看到针对不同的宏打印出了不同的值。

演示不同的exit值

#include "apue.h"
#include <sys/wait.h>int
main(void)
{pid_t   pid;int     status;if ((pid = fork()) < 0)err_sys("fork error");else if (pid == 0)              /* child */exit(7);if (wait(&status) != pid)       /* wait for child */err_sys("wait error");pr_exit(status);                /* and print its status */if ((pid = fork()) < 0)err_sys("fork error");else if (pid == 0)              /* child */abort();                    /* generates SIGABRT */if (wait(&status) != pid)       /* wait for child */err_sys("wait error");pr_exit(status);                /* and print its status */if ((pid = fork()) < 0)err_sys("fork error");else if (pid == 0)              /* child */status /= 0;                /* divide by 0 generates SIGFPE */if (wait(&status) != pid)       /* wait for child */err_sys("wait error");pr_exit(status);                /* and print its status */exit(0);
}

输出:

解释图如下:

使用wait函数的缺陷和改进-waitpid

wait函数不管共有几个子进程,只要有一个子进程终止就马上终止了,如果要等待一个指定的进程终止(如果知道要等待进程的ID),就没有办法了,此时应该使用waitpid。 回顾一下waitpid函数

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statloc, int options);

参数解释

其中statloc用法和wait相同,pid是我们需要等待的进程。作用如下:

参数pid

  1. pid==-1 等待任一进程,这种情况waitpid和wait相同
  2. pid>0 等待进程ID与pid相等的子进程
  3. pid==0 等待组ID等于调用进程组ID的任一子进程。
  4. pid<-1 等待组ID等于pid绝对值的任一子进程

参数options

options参数使我们能进一步控制waitpid操作

回值: 函数的返回值是终止子进程的进程ID,并将该子进程的终止状态存放在由statloc指向的存储单元中。

fork一个子进程但是不等待僵死进程的办法

回忆之前介绍的僵死进程:在UNIX属于中,一个已经终止、但是其父进程尚未对其进行善后处理的进程的进程被称为僵死进程。 如果一个进程fork一个子进程但是不要它等待子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一要求的诀窍:调用fork两次

举例:

#include "apue.h"
#include <sys/wait.h>int
main(void)
{pid_t   pid;if ((pid = fork()) < 0) {err_sys("fork error");} else if (pid == 0) {      /* first child */if ((pid = fork()) < 0)err_sys("fork error");else if (pid > 0)exit(0);    /* parent from second fork == first child *//** We're the second child; our parent becomes init as soon* as our real parent calls exit() in the statement above.* Here's where we'd continue executing, knowing that when* we're done, init will reap our status.*/sleep(2);printf("second child, parent pid = %ldn", (long)getppid());exit(0);}if (waitpid(pid, NULL, 0) != pid)   /* wait for first child */err_sys("waitpid error");/** We're the parent (the original process); we continue executing,* knowing that we're not the parent of the second child.*/exit(0);
}

执行结果:

解释: 如果了解上一个例题之后这个例题其实很简单

可以看到,子进程2打印的parent ID是1,也就是init进程,这个时候直接是由init进程托管的,不会出现僵死进程。 注意: 子进程1 fork之后子进程1和子进程2的执行顺序是不确定的。因此若是先执行子进程2,那么马上调用了sleep,执行到子进程1时马上终止。若是先执行子进程1则马上终止,执行到子进程2时sleep,这样可以确保无论什么顺序子进程2 sleep结束之后子进程1已经终止

7 函数waitid

waitid函数类似waitpid,但是提供了更多的灵活性。 函数形式:

#include <sys/wait.h>int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

返回值: 若成功返回0,出错返回-1.

与waitpid相似,waitid允许一个进程指定要等待的子进程,但它使用两个单独的参数表示要等待的子进程所属的类型,而不是将此与进程ID或进程组ID组成一个参数。id参数的作用于idtype的值相关。

fork()函数_UNIX环境高级编程(APUE)系列学习第8章-2 exit系列函数与wait系列函数...相关推荐

  1. (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  2. Linux环境高级编程函数,Linux环境高级编程--出错处理(CLStatus)

    很多程序库对外提供若干类,每个方法出错时如何告知调用者是否出错,以及出错码(在Linux上在error.h中的全局errno就是保存我们Linux程序执行的出错码的)?方法很多,为了简化起见,函数将返 ...

  3. execlp使用例子_Unix环境高级编程-execlp示例问题

    最近看Unix环境高级编程,test 1.6节的代码的时候发现无效果, #include "apue.h" #include int main(void) { char buf[M ...

  4. 《UNIX环境高级编程》笔记 第五章-标准IO库

    1. 流和FILE对象 在第三章的系统调用都是围绕文件描述符fd的.但是标准I/O库函数操作则是围绕流进行的.当使用标准I/O库打开或创建一个文件时,使用一个流与一个文件关联. 当打开一个流时,标准I ...

  5. 《UNIX环境高级编程》笔记 第十三章-守护进程

    1. 概念 守护进程(daemon)是生存期长的一种进程.它们常常在系统引导装入时启动,仅在系统关闭时才终止.因为它们没有控制终端,所以说它们是在后台运行的. Linux的大多数服务就是用守护进程实现 ...

  6. apue.h头文件(UNIX环境高级编程)

    相信非常多初学<UNIX环境高级编程>的朋友都会遇到一个问题,执行里面的实例(download: http://www.apuebook.com/ )时就出现故障,提示 "错误: ...

  7. Unix——学习《Unix环境高级编程》找不到“apue.h”方法

    在运行<UNIX环境高级编程>中的程序时会遇到apue.h包头找不到的情况,这是作者为了方便程序书写封闭了一些功能函数和错误处理等.在http://www.apuebook.com中可以下 ...

  8. UNIX环境高级编程(第三版)关于apue.h的用法

    UNIX环境高级编程(第三版)中的例子用到apue.h这个头文件,但是书里面写的地址已经不能访问. 经过一番查找之后,找到如下解决方案: 1.到www.apuebook.com上下载第2版的源码,也可 ...

  9. Unix环境高级编程中的apue.h配置

    本文解释Unix环境高级编程的环境搭建中,apue.h如何使用安装及问题解决. 使用的是<Unix环境高级编程>第三版: 系统为CentOS 7.4,64位(Linux系统都可参考): 文 ...

最新文章

  1. 和至少为k的最短子数组 python_LeetCode 862. 和至少为 K 的最短子数组
  2. 中美程序员的不完全对比,看看跟你了解的一样吗?
  3. 【C 语言】结构体相关 的 函数 指针 数组
  4. 查看文档(API) (NSString)
  5. linux运行cmd文件sh文件目录,在Windows上,如何执行.sh文件?
  6. 网上报名的一些感慨.
  7. Python 字典删除元素clear、pop、popitem
  8. 数据结构实验之图论二:基于邻接表的广度优先搜索遍历
  9. 如何组合来自多个SQL表的结果(提示:有三种方式)
  10. [leetcode]5366. 检查网格中是否存在有效路径
  11. 洛谷 P1144 最短路计数 解题报告
  12. win10计算器rsh_Win10计算器快捷键
  13. mc服务器小地图不显示玩家,为啥小地图莫名消失了 玩家:或许是它喝完随机饮料后 自己隐身了...
  14. 微分中值定理与导数的应用
  15. python英文文本情感分析_sentimentpy模块进行中文文本情感分类
  16. 小程序转uni-app——条件判断包含中文
  17. 美团-外卖骑手背后的AI技术
  18. 学习笔记(16):重叠元素
  19. 【数学分析】Bolzano-Weierstrass定理及其证明(有界数列一定存在收敛子列)
  20. 导入MVVMLight出现错误 ViewModelLocator does not exist in the namespace clr-namespace:WpfApp1.ViewModel

热门文章

  1. c替换指定位置字符串_【JavaScript】字符串
  2. PHP大型Web应用入门(一)
  3. realme真我GT2系列节后登场:出厂自带realme UI 3.0正式版
  4. 丁磊:网易在元宇宙技术和规划上已经做好准备
  5. 微信封杀lol手游活动小程序?《英雄联盟手游》回应了
  6. QQ解除外链限制,支持直接跳转淘宝抖音
  7. 董小姐宣布重磅升级:格力空调包修时长正式升级为10年
  8. 三星Galaxy S21+真机上手视频曝光:外观彻底无悬念
  9. 荣耀V40渲染图曝光 将搭载双曲面瀑布屏
  10. 股市太火 !“牛市”搜索热度暴涨9倍:80后疯狂开户