之前一直没太深入的去理解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. hdoj:2029

    #include <iostream> #include <string> using namespace std; bool isPalindromes(string s) ...

  2. Opencv Mat运算(转)

    一.矩阵Mat I,img,I1,I2,dst,A,B; double k,alpha; Scalar s; //注意Mat的行列号是从0开始的 //定义矩阵a,b,c Mat a,b,c; //生成 ...

  3. ubuntu安装anaconda后,终端输入conda,出现未找到命令

    解决办法: 终端输入:vim  ~/.bashrc 键盘大写“G”,在最末端输入:export PATH=~/anaconda2/bin:$PATH 使其生效:source  ~/.bashrc 打印 ...

  4. [Artoolkit] Framework Analysis of nftSimple

    What is nftSimple? Loads NFT dataset names from a configuration file. The example uses the “Pinball. ...

  5. 12外观模式Facade

    一.什么是外观模式 Facade模式也叫外观模式,是由GoF提出的 23种设计模式中的一种.Facade模式为一组具 有类似功能的类群,比如类库,子系统等等,提供一个一致的简单的界面.这个一致的简单的 ...

  6. Hibernate -- Dao层 -- CURD -- 随记

    根据Where 参数 查询记录总数 .拼接SQL语句 .获取Session(hibernateTemplate.getSessionFactory().getCurrentSession()),调用C ...

  7. c __cplusplus详解

    Code:#ifdef __cplusplusextern "C" { #endif ... #ifdef __cplusplus} #endif 解释:1.c++中定义了__cp ...

  8. SpringBoot Docker Mysql安装,Docker安装Mysql

    SpringBoot Docker Mysql安装,Docker安装Mysql ================================ ©Copyright 蕃薯耀 2018年4月8日 ht ...

  9. 【代码审计】XYHCMS V3.5任意文件下载漏洞分析

      0x00 环境准备 XYHCMS官网:http://www.xyhcms.com/ 网站源码版本:XYHCMS V3.5(2017-12-04 更新) 程序源码下载:http://www.xyhc ...

  10. Hook lua库函数时遇到的问题

    最近在为distri.lua实现一个lua调试系统,有一个简单的需求,lua导入一个文件的时候,将这个文件的文件名记录下来, 以方便调试器在设置断点的时候判断是否一个合法的文件. lua导入文件是通过 ...