Linux内核学习笔记,linux下的僵尸进程管理SIGCHLD复信号

图片 9
亚洲城ca88手机版官网

  进程调用 exit() 退出实施后,被设置为僵死状态,那时父进程能够透过
wait4()
系统调用查询子进程是或不是终止,之后再进行末段的操作,通透到底去除进度所占有的内部存款和储蓄器能源。
wait四() 系统调用由 linux 内核算现,linux 系统平时提供了
wait()、waitpid()、wait三()、wait肆()
那八个函数,四个函数的参数区别,语义也有微小的差异,然则都回去关于结束进程的情景新闻。

  Linux中wait的用法:

转自:

1、wait() 函数:

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

如何是僵尸进度

  wait() 函数的原型是:

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

首先内核会释放终止进度(调用了exit系统调用)所运用的具有存款和储蓄区,关闭全数张开的文本等,但基本为每1个终止子进度保存了区区的新闻。那几个信息至少包括进度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
就会搜聚那几个子进程的音信,并将其根本销毁后回到;要是未有找到那样2个子经过,wait
就会平昔不通在此处,直到出现僵尸进程。

      pid_t wait(int *status);

别的3个子进度(init除此之外)在exit后不要立时就未有,而是留给二个称外僵尸进度的数据结构,等待父进度处理。那是各个子进程都少不了经历的级差。别的子进度退出的时候会向其父进度发送三个SIGCHLD非确定性信号。

  参数 status 保存着子进度退出时的某些状态(包括task_struct、thread_info及内核栈等)它是3个针对性 int
类型的指针;若是不在意子进度的截至状态值,只想把这一个僵尸进程消灭掉(实际上,大许多时候都以那样做的),则足以将以此参数设为
NULL,即:

  进度1旦调用了wait就及时阻塞本身,由wait自动分析是不是当前进度的有个别子进程已经推出,纵然让它找到了这么二个业已变为僵尸的子进度,wait就会征集这一个子进度的音信,并把它彻底销毁后回去;假诺没有找到那样1个子进程,wait就会一贯不通在此间,直到有贰个出现了断。

 

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

  参数status用来保存被采访进度退出是的片段状态,他是2个对准int类型的指针。但要是大家对这些子进度是什么死掉并不在意,只想把那一个僵尸进度消灭掉,我们得以设定这几个参数为NULL,

僵尸进程的目标?

  若是 wait()
调用成功,则会回去被搜集子进程的过程ID;假使被调用进程未有子进度,则调用战败,重返-1

pid=wait(NULL);

安装僵死状态的目标是维护子进度的消息,以便父进度在后来有个别时候获得。这一个音讯至少包蕴经过ID,进程的终止处境,以及该进度使用的CPU时间,所以当终止子进度的父进度调用wait或waitpid时就足以博得那些音讯。要是四个进度终止,而该进度有子进程处于僵尸状态,那么它的保有僵尸子进度的父进度ID将被重新载入参数为1(init进度)。承继那些子进度的init进度将清理它们(也正是说init进度将wait它们,从而去除它们的僵尸状态)。

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

若果撤销成功,wait会重返被搜集的子进度的历程ID,借使调用进程未有子进度,调用就会停业,此时wait重临-一,同时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 %d\n",getpid());
 15         sleep(3);
 16     }
 17     else{
 18         rpid = wait(NULL);          // 如果 wait()调用成功,则返回子进程的PID;如果调用失败,则返回 -1
 19         printf("Catch the child process with pid of %d\n",rpid);
 20     }
 21     exit(0);
 22 }    

  若是参数status的值不是NULL,wait就会把子程序退出时的场合抽出并存入在那之中,那是3个

什么制止僵尸进度?

出口结果如下:

整形值(int),提出了子进度是不奇怪退出依旧被非通常结束的,以及平常结束时的重临值,或被哪些时域信号甘休的等新闻。由于这个音信被寄存在三个整数的不等二进制位中,所以用常规的措施读取会变得要命麻烦,人们就布置了尤其的宏(macro)来成功那项工作,上边是中间常用的三个:

  1. 通过signal(SIGCHLD,
    SIG_IGN)文告内核查子进度的利落不关切,由基本回收。如若不想让父进程挂起,能够在父进度中投入一条语句:signal(SIGCHLD,SIG_IGN);表示父进度忽略SIGCHLD连续信号,该数字信号是子进程退出的时候向父进度发送的。
  2. 父进程调用wait/waitpid等函数等待子进度截至,如若尚无子进度退出wait会导致父进度阻塞waitpid能够由此传递WNOHANG使父进度不打断立刻再次回到
  3. 1经父进度很忙能够用signal注册实信号管理函数,在时限信号管理函数调用wait/waitpid等待子进度退出。
  4. 经过四回调用fork。父进程首先调用fork创设多个子进度然后waitpid等待子进度退出,子进程再fork三个孙进度后退出。那标准进度退出后会被父进度等待回收,而对于儿子进度其父进程早已退出所以孙进度成为多个孤儿进度,孤儿进度由init进度接管,孙进程甘休后,init会等待回收。

图片 1

1,WIFEXITED(status)这么些宏用来提议子进程是还是不是为健康退出的,如若是,它会回来多个非零值。(此处的status是指status指针所针对的平头)

首先种方法忽视SIGCHLD功率信号,那常用于并发服务器的属性的二个才具因为并发服务器平日fork多数子进度,子进度终结之后必要服务器进程去wait清理能源。如若将此功率信号的管理情势设为忽略,可让内核把僵尸子进度转交给init进程去管理,省去了多量僵尸进度占用系统财富。

   关于 status
参数,相比较复杂,临时不做探究,能够参考这里:

②,WEXITSTATUS(status)当以此宏重临非零值时,大家能够用那几个宏来提取子进度的重回值,

 

 

假如子进度调用exit(5)退出,WEXITSTATUS就会回去伍;如若经过不是健康退出,也便是说

僵尸进度管理办法

2、waitpid() 函数:

再次回到0,这几个值就毫无意义。

1 wait()函数

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

pid_t wait(int *status);

经过一旦调用了wait,就应声阻塞本人,由wait自动分析是还是不是当前历程的某部子进程已经淡出,假如让它找到了那样三个已经成为僵尸的子进程,wait就会采集这几个子进度的信息,并把它根本灭绝后回来;假如未有找到这么二个子历程,wait就会平昔不通在此间,直到有一个出现了断。 
参数status用来保存被采访进度退出时的一些场地,它是1个针对int类型的指针。但万一大家对那么些子进度是哪些死掉的毫不在意,只想把那么些僵尸进程消灭掉,(事实上绝大许多意况下,大家都会这样想),我们就能够设定那个参数为NULL,就象上面那样:

  pid = wait(NULL);

假定成功,wait会重临被采访的子进度的进程ID,假如调用进度没有子进度,调用就会破产,此时wait重回-1,同时errno被置为ECHILD。

  • wait系统调用会使父进度暂停奉行,直到它的3个子过程截至结束。
  • 回到的是子进度的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,退步重临-壹

#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的任1子进度。换句话说是与调用者进度同在二个组的进程。

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

   waitpid() 函数的功力与 wait() 的功力周围,可是,它比 wait()
函数多了多个参数:

  #include <sys/wait.h>

wait与waitpid区别:

  • 在一个子进程终止前, wait 使其调用者阻塞,而waitpid
    有一选取项,可使调用者不封堵。
  • waitpid并不等待第3个终止的子进度—它有多少个选择项,能够决定它所等待的一定进度。
  • 实际上wait函数是waitpid函数的二个特例。waitpid(-一, &status, 0);

 

示例:

如以下代码会成立玖拾陆个子进程,不过父进程并未有等待它们甘休,所以在父进度退出前会有九17个僵尸进度。

图片 3

#include <stdio.h>  
#include <unistd.h>  

int main() {  

  int i;  
  pid_t pid;  

  for(i=0; i<100; i++) {  
    pid = fork();  
    if(pid == 0)  
      break;  
  }  

  if(pid>0) {  
    printf("press Enter to exit...");  
    getchar();  
  }  

  return 0;  
}  

图片 4

里头二个化解措施正是编写一个SIGCHLD复信号管理程序来调用wait/waitpid来等待子进度重返。

 

图片 5

#include <stdio.h>  
#include <unistd.h>  
#include <signal.h>  
#include <sys/types.h>  
#include <sys/wait.h>  

void wait4children(int signo) {  

  int status;  
  wait(&status);  

}  

int main() {  

  int i;  
  pid_t pid;  

  signal(SIGCHLD, wait4children);  

  for(i=0; i<100; i++) {  
    pid = fork();  
    if(pid == 0)  
      break;  
  }  

  if(pid>0) {  
    printf("press Enter to exit...");  
    getchar();  
  }  

  return 0;  
}  

图片 6

只是透过运营程序意识依然会有僵尸进程,而且每趟僵尸进程的数码都不定。那是干什么呢?其实根本是因为Linux的复信号机制是不排队的,倘使在某一时间段四个子进程退出后都会时有发生SIGCHLD功率信号,但父进度来比不上叁个1个地响应,所以末了父进度实际只进行了叁次复信号管理函数。但进行二次复信号管理函数只等待一个子历程退出,所以最终会有一些子进度照旧是僵尸进度。

就算这么只是有几许是知情的,正是收纳SIGCHLD必然有子进度退出,而我们能够在复信号管理函数里循环调用waitpid函数来等待全体的退出的子进程。至于何以不要wait,主因是在wait在清理完全数僵尸进度后再也等待会阻塞。

 

就此最好方案如下:

图片 7

#include <stdio.h>  
#include <unistd.h>  
#include <signal.h>  
#include <errno.h>  
#include <sys/types.h>  
#include <sys/wait.h>  

void wait4children(int signo) {  
  int status;  
  while(waitpid(-1, &status, WNOHANG) > 0);  
}  

int main() {  

  int i;  
  pid_t pid;  

  signal(SIGCHLD, wait4children);  

  for(i=0; i<100; i++) {  
    pid = fork();  
    if(pid == 0)  
      break;  
  }  

  if(pid>0) {  
    printf("press Enter to exit...");  
    getchar();  
  }  

  return 0;  
}  

图片 8

此地运用waitpid而不是选择wait的原由在于:咱们在2个循环内调用waitpid,以赚取具有已终止子进程的情状。大家务必钦赐WNOHANG选项,它告诉waitpid在有未有暂息的子进度在运营时决不阻塞。大家不能够在循环内调用wait,因为尚未办法幸免wait在正运维的子进程尚有未休息时打断。

1)参数 pid 为欲等待的子进度的识别码:

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

  pid < -1 ;等待历程组 ID 为 pid 相对值的进程组中的任何子进度;

  pid>0时,只等待历程ID等于pid的子进度,不管其余已经有多少子进度运维停止退出了,只要

  pid = -一 ;等待任何子进度,此时 waitpid() 也正是wait()。实际上,wait()就是 pid = -1、options = 0
的waitpid(),
 且有:

钦点的子进度还从未终止,waitpid就会直接等下去。

static inline pid_t wait(*status){
    return waitpid(-1,*status,0);  
}

  pid=-1时,等待别的一个子历程退出,未有其余限制,此时waitpid和wait的成效同样。

  pid = 0 ;等待过程组 ID
与近期进程一样的任何子进度(也等于等待同2个进度组中的任何子进度);

  pid=0时,等待同二个进度组中的任何子进度,借使子进度壹度进入其余进程组,waitpid不

  pid > 0 ;等待别的子进度 ID 为 pid
的子进度,只要钦定的子进度还一向不终止,waitpid() 就会直接等下去。

会对它做别的轮理货公司睬。

2)参数 options 提供一些额外的选项来决定 waitpid():

  pid<-一时,等待二个钦定进程组中的任何子进度,那一个历程组的ID等于pid的相对值。

  WNOHANG;假若未有其余已经终止了的子进程,则即时赶回,不等待;

options:

  WUNTRACED;要是实进度进入暂停试行的状态,则立即回到,但收尾状态不予理会;

  假如使用了WNOHANG参数调用waitpid,即便未有子进程退出,它也会立马回到,不像wait

  也足以将那四个选项组合起来使用,使用 OMurano操作。要是不想利用那三个选拔,也得以一向把 options 设为0 ,如下:

那样永世等下去。

waitpid(-1,NULL,WNOHANG | WUNTRACED);     // 没有任何已结束了的子进程或子进程进入暂停执行的状态,则马上返回不等待
waitpid(-1,NULL,0);                // options 设为0,则 waitpid() 会一直等待,直到有进程退出

  waitpid返回值:

三)waitpid() 的重回值,有三种:

  当平时重回的时候,waitpid再次来到收罗到的子进度的进度ID;

a)常常重临时,waitpid() 重临采集到的子进程的PID;

  假诺设置了采取WNOHANG,而调用waitpid发现并未已经淡出的子进程可搜罗,则重返0;

b)要是设置了 WNOHANG,而调用 waitpid()
时,未有发觉已脱离的子进程可搜罗,则重临0;

  假使调用中出错,则赶回-1,那时errno会被设置成相应的值以提醒错误的随处;当pid所

c)假如调用出错,则赶回 -1,那时erron
会被设置为相应的值以提示错误所在。(当 pid
所提醒的子进度不错在,或此进程存在,但不是调用进度的子进度, waitpid()
就会重返出错,那时 erron 被设置为 ECHILD)

指令的子进程不设有,或此进度存在,但不是调用进程的子进程,waitpid就会出错重返,那时

 

errno棉被服装置成ECHILD。

  1 #include <sys/types.h> 
  2 #include <sys/wait.h>
  3 #include <unistd.h>
  4 #include <stdio.h>
  5 #include <stdlib.h>
  6
  7 void main(){
  8     pid_t fpid,rpid;                          // fpid为fork()的返回值,rpid为waitpid()的返回值
  9     fpid = fork();
 10     if(fpid < 0){
 11         printf("error on forking");
 12     }
 13     else if(fpid == 0){                       // 子进程中 fork() 返回值为0
 14         printf("this is a child process,pid is %d\n",getpid());
 15         sleep(10);                            // 睡眠10s,10s 后子进程退出
 16         exit(0);
 17     }
 18     do{                                  // 父进程中,fork()返回新创建子进程的 PID
 19         rpid = waitpid(fpid,NULL,WNOHANG);    // 等待 PID = fpid 的进程(即子进程)退出,设置了WNOHANG选项,表明当没有发现已退出的子进程时不用等待直接返回,返回值为0;
 20         if(rpid == 0){                        // rpid = 0,说明没有发现已退出的子进程
 21             printf("No child exited\n");
 22             sleep(1);
 23         }
 24     }while(rpid == 0);
 25     if(fpid == rpid)                         // 成功收集了退出的子进程,返回值为被收集子进程的PID
 26         printf("successfully get child process %d\n",rpid);
 27     else
 28         printf("error!\n");
 29 }     

  

结果如下:

 

图片 9

  从结果中得以观察,在子进度休眠的十s时刻里,waitpid()
并从未直接等候,而是直接重返0,然后做团结的业务(睡眠壹s),如此重复了十次;当子进程退出时,waitpid()
收罗到退出的子进度,并再次来到所搜聚子进度的PID。

 

 3、wait3()、wait4() 函数:

  函数原型:

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

pid_t wait3(int *status,int options,struct rusage *rusage);
pid_t wait4(pid_t pid,int *status,int options,struct rusage *rusage);

   wait3() 和 wait4()
函数除了能够得到子进度景况音讯外,还足以获得子进度的能源利用音信,这几个音信是通过参数
rusage 得到的。而 wait3() 与 wait四() 之间的分别是,wait3()
等待全数进度,而 wait肆() 能够依附 pid 的值选拔要等待的子进程,参数 pid
的含义与 waitpid() 函数的平等。

 

 本文主要参照:

 

 

 

 

发表评论

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

网站地图xml地图