之前一直没太深入的去理解wait()函数,今天机缘巧合之前又看了看,发现之前没有真正的理解该函数。

众所周知,wait()函数一般用在父进程中等待回收子进程的资源,而防止僵尸进程的产生。

(In UNIX System terminology, a process that has terminated, but whose parent has not yet waited for it, is called a zombie. )

下面我就带着问题,层层深入地来分析这个函数。

当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。
因为子进程终止是个异步事件(这可以在父进程运行的任何是否发生),所以这种信号也是内核向父进程发的异步通知。
父进程可以选择忽略该信号,或者提供一个信号处理程序。
对于这种信号的系统默认动作是忽略。

调用wait()或waitpid()的父进程会发生什么情况:
a. 如果其所有子进程都还在运行,则阻塞;
  Q1:如果是一部分子进程终止,而另一部分还在运行,那么父进程还会阻塞吗?
    不会,只要有一个进程终止,wait就会返回。也就是说只要wait接收到一个SIGCHLD信号,wait()就会返回。对于两个或多个子进程的情况,需要调用wait两次或多次
b. 如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回;
c. 如果它没有任何子进程,则立即出错返回;

int wait(int* statloc);
int waitpid(pid_t pid, int* statloc, int options);

这两个函数的区别如下:
1. 在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞;
2. waitpid()并不等待在其调用之后的第一个终止的子进程,它有若干个选项,可以控制它所等待的进程;

如果一个子进程已经终止,并且是一个僵尸进程,则wait立即返回并取得该子进程的终止状态,否则wait使其调用者阻塞直到一个子进程终止。
如果调用者阻塞而且它有多个子进程,则在其一个子进程终止时,wait就立即返回。
因为wait的返回值是终止进程的进程ID,所以父进程总能知道哪一个子进程终止了。

参数statloc如果不是一个空指针,则终止进程的终止状态就存放在statloc所指向的单元。
参数statloc如果是一个空指针,则表示父进程不关心子进程的终止状态。

Q2:statloc中不同的值具体表示什么含义呢?
wait的输出参数statloc中,某些位表示退出状态(正常返回),其它位则指示信号编号(异常返回),有一位指示是否产生了一个core文件等等。

有四个互斥的宏可用来取得进程终止的原因。
1. WIFEXITED(status):若为正常终止子进程返回的状态,则为真。WEXITSTATUS(status)可取得子进程传送给exit、_exit或_Exit参数的低8位;
2. WIFSIGNALED(status):若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号,如SIGCHLD)。对于这种情况,可执行WTERMSIG(status),取得是子进程终止的信号的编号。
另外,有些实现(非Single UNIX Specification)定义宏WCOREDUMP(status),若已产生终止进程的core文件,则它返回真。
3. WIFSTOPPED(status):若为当前暂停的子进程的返回的状态,则为真。对于这种情况,可执行WSTOPSIG(status),取使子进程暂停的信号编号。
4. WIFCONTINUED(status):若在作业控制暂停后已经继续的子进程返回了状态,则为真.(POSIX.1的XSI扩展,仅用于waitpid)

Q3: wait函数和SIGCHLD信号的关系?两者之间的关系,需要分成三个问题

已知系统默认是忽略SIGCHLD信号,在一个进程终止或停止时,会将SIGCHLD信号发送给其父进程。
已知父进程若不调用wait()获取子进程的终止状态,那么子进程就会变成僵尸进程。

Q3.1:wait()是关于是否产生僵尸进程的问题。

Q3.2:SIGCHLD信号是关于自己本身的处理方式的选择问题。

当这两个问题(Q3.1&Q3.2)结合在一起应用时,就产生了另外一个问题,父进程是同步还是异步的问题(或者描述为阻塞还是非阻塞问题)
当SIGCHLD的处理方式是系统默认时,父进程调用了wait()以防止子进程变成僵尸进程,那么父进程必须等待子进程结束之后才能执行wait()之后的流程,即同步问题。
当SIGCHLD的处理方式是捕获时,在其信号处理程序中调用wait()函数,就能获取子进程的终止状态而不产生僵尸进程同时父进程并不会阻塞,做自己想做的事,即异步问题。

Q3.3:wait什么时候返回的问题,wait()的返回和SIGCHLD有什么关系?

根据wait()的描述:
All of these system calls are used to wait for state changes in a child of the calling process, and obtain information about the child whose
state has changed. A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by a
signal. In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a wait is
not performed, then terminated the child remains in a "zombie" state (see NOTES below).

If a child has already changed state, then these calls return immediately. Otherwise they block until either a child changes state or a signal
handler interrupts the call (assuming that system calls are not automatically restarted using the SA_RESTART flag of sigaction(2)). In the
remainder of this page, a child whose state has changed and which has not yet been waited upon by one of these system calls is termed waitable.
当子进程的状态发生改变时,wait()返回;
当调用wait()的进程接收到一个被设置为SA_INTERRUPT的信号时,wait()返回;
因为SIGCHLD信号的产生必然是伴随着子进程状态的改变,所以当有SIGCHLD信号发生时,wait会返回。

 #include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h> void print_exit(int status)
{
if (WIFEXITED(status))
printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status),
#ifdef WCOREDUMP
WCOREDUMP(status) ? ("core file generated") : (""));
#else
"");
#endif
else if (WIFSTOPPED(status))
printf("child stopped, signal number=%d\n", WSTOPSIG(status));
} void sig_child(int signo)
{
int status;
int ret;
ret = wait(&status);
printf("pid:%d, res:%d, status=%d, %s\n", getpid(), ret, status, strerror(errno));
print_exit(status);
} void sig_usr(int signo)
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
printf("received signal %d\n", signo);
} int main(int argc, char** argv)
{
pid_t pid;
int status;
int ret;
int remaintime=;
struct sigaction act, oact;
sigset_t oldmask; //signal(SIGCHLD, sig_child);
//signal(SIGUSR1, sig_usr); act.sa_handler = sig_usr;
sigemptyset(&act.sa_mask);
act.sa_flags = |SA_INTERRUPT;
sigaction(SIGUSR1, &act, &oact); if ((pid=fork()) < )
{
printf("fork error\n");
return -;
}
else if (pid == )
{ printf("child:pid:%d\n", getpid());
remaintime = sleep();
printf("remiantime=%d\n", remaintime);
//exit(0);
//return 0;
//
//sleep(30);//SIGQUIT
}
else
{
printf("father:pid:%d\n", getpid());
//while(1)
//{
// sleep(1);
// printf("1111\n");
//}
ret = wait(&status);
printf("res:%d, status=%d, %s\n", ret, status, strerror(errno));
print_exit(status);
} return ;
}

wait()函数的详细分析的更多相关文章

  1. LinkedList详细分析

    一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...

  2. C语言中的static 详细分析

    转自:http://blog.csdn.net/keyeagle/article/details/6708077/ google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇大 ...

  3. lk启动流程详细分析

    转载请注明来源:cuixiaolei的技术博客 这篇文章是lk启动流程分析(以高通为例),将会详细介绍下面的内容: 1).正常开机引导流程 2).recovery引导流程 3).fastboot引导流 ...

  4. Android-Native-Server 启动和注册详细分析

    Android-Native-Server 启动和注册详细分析     以mediaService为实例来讲解: mediaService的启动入口 是一个 传统的  main()函数 源码位置E:\ ...

  5. Http Pipeline详细分析(下)

    Http Pipeline详细分析(下) 文章内容 接上面的章节,我们这篇要讲解的是Pipeline是执行的各种事件,我们知道,在自定义的HttpModule的Init方法里,我们可以添加自己的事件, ...

  6. HashMap 源码详细分析(JDK1.8)

    一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...

  7. 海思uboot启动流程详细分析(三)【转】

    1. 前言 书接上文(u-boot启动流程分析(二)_平台相关部分),本文介绍u-boot启动流程中和具体版型(board)有关的部分,也即board_init_f/board_init_r所代表的. ...

  8. scrapy爬虫具体案例步骤详细分析

    scrapy爬虫具体案例详细分析 scrapy,它是一个整合了的爬虫框架, 有着非常健全的管理系统. 而且它也是分布式爬虫, 它的管理体系非常复杂. 但是特别高效.用途广泛,主要用于数据挖掘.检测以及 ...

  9. 详细分析MySQL事务日志(redo log和undo log)

    innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...

随机推荐

  1. 执行一条SQL语句,插入多条数据!

    insert into blog (bid, aid) values (2,1)(2,2)(2,3)

  2. 【GMT43智能液晶模块】例程四:SYSTICK定时器——定时读取触摸值

    实验原理: 本实验采用系统定时器,通过简单的初始化定时20ms,每20ms读取一次触 摸值,并基于emWin的UI界面将读到的触摸值显示在界面上. 实验现象: 源代码下载链接: 链接:http://p ...

  3. 【emWin】例程十四:xbf外置字体

    介绍: 本例将xbf格式文件放到SD卡中,通过读取SD卡中的字库文件在液晶上显示文字.   实验指导书及代码包下载: 链接:http://pan.baidu.com/s/1mhTdYeG 密码:aka ...

  4. 【emWin】例程三十:窗口对象——Multiedit

    简介: 本例程介绍MULTIEDIT的使用方法通过MULTIEDIT 小工具可编辑多行文本.它既 可以被用作简单的文本编辑器,也可以用来显示静态文本.该小工具支持带滚动条 和不带滚动条的滚动 触摸校准 ...

  5. SOLID原则 【转】

    S.O.L.I.D 是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则(Programming Priciple)的首字母缩写. 面向对象设计的原则 SRP  The Single ...

  6. 在android 上 使用 rxjava 入门篇

    什么是 rxJava? RxJava is a Java VM implementation of Reactive Extensions: a library for composing async ...

  7. 教你一招:Microsoft Office Word已停止工作

    1/按组合键WIN+R打开运行对话框 2/在打开框中键入%USERPROFILE%\AppData\Roaming\Microsoft\Templates,单击“确定”按钮 3/在打开的窗口鼠标右键删 ...

  8. 最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)

    最详尽的 JS 原型与原型链终极详解,没有「可能是」.(一) 第二篇已更新,点击进入第三篇已更新,点击进入

  9. TCP/IP模型及OSI七层参考模型各层的功能和主要协议

    注:网络体系结构是分层的体系结构,学术派标准OSI参考模型有七层,而工业标准TCP/IP模型有四层.后者成为了事实上的标准,在介绍时通常分为5层来叙述但应注意TCP/IP模型实际上只有四层. 1.TC ...

  10. Centos7 中lvs DR配置

    服务器主机: 10.200.3.100       DirectServer 10.200.3.99         RealServer1 10.200.3.101 RealServer2 10.2 ...