本节目标:

  • 僵进程
  • SIGCHLD
  • wait
  • waitpid

一,僵尸进程

当一个子进程先于父进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行,或者父进程调用了wait才告终止。

子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态。

进程表中代表子进程的数据项是不会立刻释放的,虽然不再活跃了,可子进程还停留在系统里,因为它的退出码还需要保存起来以备父进程中后续的wait调用使用。它将称为一个“僵进程”。

二,如何避免僵尸进程

  • 调用wait或者waitpid函数查询子进程退出状态,此方法父进程会被挂起。
  • 如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。

三,SIGCHLD信号

当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止)

如果不想让子进程编程僵尸进程可在父进程中加入:signal(SIGCHLD,SIG_IGN);

如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。

示例:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h> int main(void)
{
pid_t pid;
if(signal(SIGCHLD,SIG_IGN) == SIG_ERR)
{
perror("signal error");
exit(EXIT_FAILURE);
}
pid = fork();
if(pid == -1)
{
perror("fork error");
exit(EXIT_FAILURE);
}
if(pid == 0)
{
printf("this is child process\n");
exit(0);
}
if(pid > 0)
{
sleep(100);
printf("this is parent process\n");
}
return 0;
}

结果:

可知,虽然子进程先退出了,但进程表中已经不存在子进程的僵尸状态

 

三,wait()函数

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int *status);

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:

		pid = wait(NULL);	

如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

man帮助:

DESCRIPTION

       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 ter-

       minated; 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 the terminated child remains in a "zombie"

       state (see NOTES below).

       If a child has already changed state, then these calls  return  immedi-

       ately.   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() :

    The wait() system call suspends execution of the calling process  until

    one  of  its children terminates.  The call wait(&status) is equivalent

    to:

        waitpid(-1, &status, 0);

If status is not NULL, wait() and waitpid() store status information in

      the  int  to  which  it points.  This integer can be inspected with the

      following macros (which take the integer itself as an argument,  not  a

      pointer to it, as is done in wait() and waitpid()!):

      WIFEXITED(status)

             returns true if the child terminated normally, that is, by call-

             ing exit(3) or _exit(2), or by returning from main().

      WEXITSTATUS(status)

             returns the exit status of the  child.   This  consists  of  the

             least  significant  8 bits of the status argument that the child

             specified in a call to exit(3) or _exit(2) or  as  the  argument

             for  a  return  statement  in main().  This macro should only be

             employed if WIFEXITED returned true.

      WIFSIGNALED(status)

             returns true if the child process was terminated by a signal.

    WTERMSIG(status)

             returns the number of the signal that caused the  child  process

             to terminate.  This macro should only be employed if WIFSIGNALED

             returned true.

      WCOREDUMP(status)

             returns true if the child produced  a  core  dump.   This  macro

             should  only  be  employed  if  WIFSIGNALED returned true.  This

             macro is not specified in POSIX.1-2001 and is not  available  on

             some  Unix  implementations  (e.g.,  AIX, SunOS).  Only use this

             enclosed in #ifdef WCOREDUMP ... #endif.

      WIFSTOPPED(status)

             returns true if the child process was stopped by delivery  of  a

             signal;  this  is  only possible if the call was done using WUN-

             TRACED or when the child is being traced (see ptrace(2)).

      WSTOPSIG(status)

             returns the number of the signal which caused the child to stop.

             This  macro should only be employed if WIFSTOPPED returned true.

   WIFCONTINUED(status)

       (since Linux 2.6.10) returns  true  if  the  child  process  was

       resumed by delivery of SIGCONT.

  • wait系统调用会使父进程暂停执行,直到它的一个子进程结束为止。
  • 返回的是子进程的PID,它通常是结束的子进程
  • 状态信息允许父进程判定子进程的退出状态,即从子进程的main函数返回的值或子进程中exit语句的退出码。
  • 如果status不是一个空指针,状态信息将被写入它指向的位置

可以上述的一些宏判断子进程的退出情况:

示例程序:

#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h> int main(void)
{
pid_t pid;
pid = fork();
if(pid < 0){
perror("fork error");
exit(EXIT_FAILURE);
}
if(pid == 0){
printf("this is child process\n");
exit(100);
} int status;
pid_t ret;
ret = wait(&status);
if(ret <0){
perror("wait error");
exit(EXIT_FAILURE);
}
printf("ret = %d pid = %d\n", ret, pid);
if (WIFEXITED(status))
printf("child exited normal exit status=%d\n", WEXITSTATUS(status)); else if (WIFSIGNALED(status))
printf("child exited abnormal signal number=%d\n", WTERMSIG(status));
else if (WIFSTOPPED(status))
printf("child stoped signal number=%d\n", WSTOPSIG(status));
return 0;
}

结果:

当子进程正常退出时wait返回子进程pid,且WIFEXITED(status)验证为真,可以WEXITSTATUS(status)获得返回状态码

示例2:

#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h> int main(void)
{
pid_t pid;
pid = fork();
if(pid < 0){
perror("fork error");
exit(EXIT_FAILURE);
}
if(pid == 0){
printf("this is child process\n");
//exit(100);
abort();
} int status;
pid_t ret;
ret = wait(&status);
if(ret <0){
perror("wait error");
exit(EXIT_FAILURE);
}
printf("ret = %d pid = %d\n", ret, pid);
if (WIFEXITED(status))
printf("child exited normal exit status=%d\n", WEXITSTATUS(status)); else if (WIFSIGNALED(status))
printf("child exited abnormal signal number=%d\n", WTERMSIG(status));
else if (WIFSTOPPED(status))
printf("child stoped signal number=%d\n", WSTOPSIG(status));
return 0;
}

结果:

当子进程异常退出时,WIFSIGNALED(status)为真,可用WTERMSIG(status)获得信号

 

四,waitpid()函数

#include <sys/types.h>

#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

参数:

status:如果不是空,会把状态信息写到它指向的位置,与wait一样

options:允许改变waitpid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起

The value of options is an OR of zero or more  of  the  following  con-

stants:

WNOHANG     return immediately if no child has exited.

WUNTRACED   also  return  if  a  child  has stopped (but not traced via

            ptrace(2)).  Status for traced children which have  stopped

            is provided even if this option is not specified.

WCONTINUED (since Linux 2.6.10)

            also return if a stopped child has been resumed by delivery

            of SIGCONT.

返回值:如果成功返回等待子进程的ID,失败返回-1

对于waitpid的p i d参数的解释与其值有关:

pid == -1 等待任一子进程。于是在这一功能方面waitpid与wait等效。

pid > 0 等待其进程I D与p i d相等的子进程。

pid == 0 等待其组I D等于调用进程的组I D的任一子进程。换句话说是与调用者进程同在一个组的进程。

pid < -1 等待其组I D等于p i d的绝对值的任一子进程

wait与waitpid区别:

  • 在一个子进程终止前, wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。
  • waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的特定进程。
  • 实际上wait函数是waitpid函数的一个特例。waitpid(-1, &status, 0);

示例:

#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h> int main(void)
{
pid_t pid;
pid = fork();
if(pid < 0){
perror("fork error");
exit(EXIT_FAILURE);
}
if(pid == 0){
printf("this is child process\n");
sleep(5);
exit(100); } int status;
pid_t ret;
ret = waitpid(pid,&status,WNOHANG);
if(ret <0){
perror("wait error");
exit(EXIT_FAILURE);
}
printf("ret = %d pid = %d\n", ret, pid);
if (WIFEXITED(status))
printf("child exited normal exit status=%d\n", WEXITSTATUS(status)); else if (WIFSIGNALED(status))
printf("child exited abnormal signal number=%d\n", WTERMSIG(status));
else if (WIFSTOPPED(status))
printf("child stoped signal number=%d\n", WSTOPSIG(status));
return 0;
}

结果:

可知,option设为WNOHANG,父进程不会等到子进程的退出,即不会阻塞,如果没有子进程退出则立即返回-1,

linux系统编程之进程(六):父进程查询子进程的退出,wait,waitpid的更多相关文章

  1. Linux系统编程(26)——守护进程

    Linux系统启动时会启动很多系统服务进程,比如inetd,这些系统服务进程没有控制终端,不能直接和用户交互.其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户 ...

  2. linux系统编程之信号(六)

    今天继续学习信号相关的知识,主要还是学习sigqueue另外信号发送函数,并配合上节学习的sigaction的用法,进入正题: sigqueue函数: sigval联合体: 实际上sigval参数是用 ...

  3. linux系统编程之框架

    linux系统编程之框架: 1. 进程 1.1 进程概念 1.1.1 PCB 1.1.2 环境变量 1.2 进程控制 1.3 进程间通信 1.3.1 管道 1.3.2 有名管道 1.3.3 共享内存 ...

  4. Linux系统编程@进程通信(一)

    进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...

  5. Linux系统编程——Daemon进程

    目录 Daemon进程介绍 前提知识 Daemon进程的编程规则 Daemon进程介绍 Daemon运行在后台也称作"后台服务进程". 它是没有控制终端与之相连的进程.它独立与控制 ...

  6. linux系统编程-进程

    进程 现实生活中 在很多的场景中的事情都是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的: 如下是一段视频,迈克杰克逊的一段视频: http://v.youku.com ...

  7. linux系统编程之进程(一)

    今天起,开始学习linux系统编程中的另一个新的知识点----进程,在学习进程之前,有很多关于进程的概念需要了解,但是,概念是很枯燥的,也是让人很容易迷糊的,所以,先抛开这些抽象的概念,以实际编码来熟 ...

  8. linux系统编程--守护进程,会话,进程组,终端

    终端: 在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal), 进程中,控制终端是保存在PCB中的信息,而f ...

  9. linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

随机推荐

  1. LuoguP1226 【模板】快速幂||取余运算

    题目链接:https://www.luogu.org/problemnew/show/P1226 第一次学快速幂,将别人对快速幂原理的解释简要概括一下: 计算a^b时,直接乘的话计算次数为b,而快速幂 ...

  2. 基于Jenkins的持续集成CI

    CI(continuous integration)持续集成 一次构建:可能包含编译,测试,审查和部署,以及其他一些事情,一次构建就是将源代码放在一起,并验证软件是否可以作为一个一致的单元运行的过程. ...

  3. 阈值分割与XLD轮廓拼接——第4讲

    一.阈值分割 阈值分割算子众多: threshold :这是最基本最简单的阈值算子. binary_threshold :它是自动阈值算子,自动选出暗(dark)的区域,或者自动选出亮(light)的 ...

  4. PAT 1079 延迟的回文数(代码+思路)

    1079 延迟的回文数(20 分) 给定一个 k+1 位的正整数 N,写成 a​k​​⋯a​1​​a​0​​ 的形式,其中对所有 i 有 0≤a​i​​<10 且 a​k​​>0.N 被称 ...

  5. PAT 1064 朋友数(20)(代码)

    1064 朋友数(20 分) 如果两个整数各位数字的和是一样的,则被称为是"朋友数",而那个公共的和就是它们的"朋友证号".例如 123 和 51 就是朋友数, ...

  6. vc到vs2015消息函数

    afx_msg LRESULT OnMyIconNotify(WPARAM wParam,LPARAM lParam); vc6 可以是void  vs2015不可以 ON_MESSAGE(MYWM_ ...

  7. 理解OAuth 2.0 (摘自阮一峰网络日志)

    OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版. 本文对OAuth 2.0的设计思路和运行流程,做一个简明通俗的解释,主要参考材料为R ...

  8. 【Linux】Jenkins配置和使用(二)

    摘要 本章介绍Jenkins的简单使用,关于Jenkins的安装,参照[Linux]Jenkins安装(一) 事例说明:在linux环境下,安装的jenkins,集成svn,tomcat的环境,项目是 ...

  9. Guava学习笔记:Preconditions优雅的检验参数(java)

    http://www.cnblogs.com/peida/p/guava_preconditions.html 在日常开发中,我们经常会对方法的输入参数做一些数据格式上的验证,以便保证方法能够按照正常 ...

  10. 594. Longest Harmonious Subsequence

    方法一:用一个map来辅助,代码简单,思路清晰 static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NULL); ; }( ...