linux下的僵尸进程处理SIGCHLD信号【转】

  进程调用 exit() 退出执行后,被设置为僵死状态,这时父进程可以通过
wait4()
系统调用查询子进程是否终结,之后再进行最后的操作,彻底删除进程所占用的内存资源。
wait4() 系统调用由 linux 内核实现,linux 系统通常提供了
wait()、waitpid()、wait3()、wait4()
这四个函数,四个函数的参数不同,语义也有细微的差别,但是都返回关于终止进程的状态信息。

  Linux中wait的用法:

转自:

1、wait() 函数:

  系统中的僵尸进程都要由wait系统调用来回收。

什么是僵尸进程

  wait() 函数的原型是:

  函数原型#include <sys/types.h>

首先内核会释放终止进程(调用了exit系统调用)所使用的所有存储区,关闭所有打开的文件等,但内核为每一个终止子进程保存了一定量的信息。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。

#include <sys/types.h>        // 提供类型 pid_t 的定义
#include <sys/wait.h>

pid_t wait(int *status);

      #include <sys/wait.h>

而僵尸进程就是指:一个进程执行了exit系统调用退出,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态)的进程。

  当进程调用 wait() 时,会暂停目前进程的执行(即阻塞),由 wait()
来自动分析是否当前进程的某个子进程已经退出,如果找到了这样一个已经变成僵尸进程的子进程,wait
就会收集这个子进程的信息,并将其彻底销毁后返回;如果没有找到这样一个子进程,wait
就会一直阻塞在这里,直到出现僵尸进程。

      pid_t wait(int *status);

任何一个子进程(init除外)在exit后并非马上就消失,而是留下一个称外僵尸进程的数据结构,等待父进程处理。这是每个子进程都必需经历的阶段。另外子进程退出的时候会向其父进程发送一个SIGCHLD信号。

  参数 status 保存着子进程退出时的一些状态(包括
task_struct、thread_info及内核栈等)它是一个指向 int
类型的指针;如果不在意子进程的结束状态值,只想把这个僵尸进程消灭掉(实际上,大多数时候都是这样做的),则可以将这个参数设为
NULL,即:

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

 

pid = wait(NULL);        // 不管子进程的结束状态,直接杀死进程

  参数status用来保存被收集进程退出是的一些状态,他是一个指向int类型的指针。但如果我们对这个子进程是如何死掉并不在意,只想把这个僵尸进程消灭掉,我们可以设定这个参数为NULL,

僵尸进程的目的?

  如果 wait()
调用成功,则会返回被收集子进程的进程ID;如果被调用进程没有子进程,则调用失败,返回
-1

pid=wait(NULL);

设置僵死状态的目的是维护子进程的信息,以便父进程在以后某个时候获取。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。如果一个进程终止,而该进程有子进程处于僵尸状态,那么它的所有僵尸子进程的父进程ID将被重置为1(init进程)。继承这些子进程的init进程将清理它们(也就是说init进程将wait它们,从而去除它们的僵尸状态)。

  接下来用一段代码来演示一下 wait() 的用法:

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

 

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 #include <stdlib.h>                                                                    
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 
  7 void main(){
  8     pid_t fpid,rpid;
  9     fpid = fork();
 10     if(fpid < 0){        
 11         perror("error on forking!n");
 12     }
 13     else if(fpid == 0){
 14         printf("this is a child process! the pid is %dn",getpid());
 15         sleep(3);
 16     }
 17     else{
 18         rpid = wait(NULL);          // 如果 wait()调用成功,则返回子进程的PID;如果调用失败,则返回 -1
 19         printf("Catch the child process with pid of %dn",rpid);
 20     }
 21     exit(0);
 22 }    

  如果参数status的值不是NULL,wait就会把子程序退出时的状态取出并存入其中,这是一个

如何避免僵尸进程?

输出结果如下:

整形值(int),指出了子进程是正常退出还是被非正常结束的,以及正常结束时的返回值,或被哪个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会变得非常麻烦,人们就设计了专门的宏(macro)来完成这项工作,下面是其中常用的两个:

  1. 通过signal(SIGCHLD,
    SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。
  2. 父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞waitpid可以通过传递WNOHANG使父进程不阻塞立即返回
  3. 如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。
  4. 通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进程其父进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。

图片 1

1,WIFEXITED(status)这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。(此处的status是指status指针所指向的整数)

第一种方法忽略SIGCHLD信号,这常用于并发服务器的性能的一个技巧因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。

   关于 status
参数,比较复杂,暂时不做讨论,可以参考这里:

2,WEXITSTATUS(status)当这个宏返回非零值时,我们可以用这个宏来提取子进程的返回值,

 

 

如果子进程调用exit(5)退出,WEXITSTATUS就会返回5;如果进程不是正常退出,也就是说

僵尸进程处理办法

2、waitpid() 函数:

返回0,这个值就毫无意义。

1 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。

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

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

图片 2

 

  函数原型:

  对于waitpid()函数来说,多出了两个可以由用户控制的参数pid和options。

2 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

#include <sys/types.h>
#include <sys/wait.h>

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

    #include <sys/types.h> /* 提供类型pid_t的定义 */

对于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的绝对值的任一子进程

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website