perl实现对各种命令的守护进程,能够自动重启进程

运维需要对一些关键的服务进程进行守护,例如tomcat进程,mysql之类,这种进程没有自己的守护进程,而我们又不可能去改它们的源代码。

为此我用perl写了一个守护进程,根据传入的命令,启动要守护的进程,若是进程挂了,则重新启动进程。
即使子进程被杀死了,也能自动起来,但是程序有点缺陷:
1. 这个守护进程只是针对那些永远不退出的进程有效。
2. 若是杀死了守护进程,被守护的进程有可能不会退出,还要手动去杀死被守护进程,才能退出。因为我们找到杀死整个进程树的方法,

 


  1. #!/usr/bin/perl  
  2.  
  3. ####################################################  
  4.  
  5. #功能:实现把传入的命令执行,并守护,当命令被杀死了,能够重新启动命令  
  6.  
  7. #系统环境:centos 5  
  8.  
  9. #编译环境:perl, v5.8.8 built for i386-linux-thread-multi  
  10.  
  11. #执行:               
  12.  
  13. #echo "i=0;" > /root/w.sh  
  14.  
  15. #echo 'while(true)' >> /root/w.sh  
  16.  
  17. #echo "do" >> /root/w.sh  
  18.  
  19. #echo ' echo $i;let i=$i+1;sleep 1;' >> /root/w.sh  
  20.  
  21. #echo 'done' >> /root/w.sh  
  22.  
  23. #perl deamon.pl "sh /root/w.sh >> /root/w.log" 
  24.  
  25. #  
  26.  
  27. ####################################################  
  28.  
  29. use POSIX ();  
  30.  
  31. use Carp;  
  32.  
  33.  
  34.  
  35. our $logfile="/tmp/deamon.log"; #输出日志  
  36.  
  37. our $child_pid; #记录子进程的id,以便父进程去杀死子进程  
  38.  
  39. our $parent_pid;#记录父进程的id,以便区分父子进程  
  40.  
  41. our $maxloop=10000; #放置大量产生子进程  
  42.  
  43. our $loop=0;  
  44.  
  45. our $CMD=$ARGV[0];  
  46.  
  47. if(!$CMD){  
  48.  
  49.     die("please input a cmd\n");  
  50.  
  51. }  
  52.  
  53.  
  54.  
  55. #杀死父子进程  
  56.  
  57. sub kill_pid{  
  58.  
  59.     logs("catch quit signal\n");  
  60.  
  61.     if($parent_pid){#判断是不是父进程  
  62.  
  63.         #父进程杀死所有的子进程  
  64.  
  65.         logs("parent kill child\n");  
  66.  
  67.         kill(15,$child_pid);  
  68.  
  69.         kill(15,-$$);  
  70.  
  71.         logs("parent quit\n");  
  72.  
  73.         exit 0;  
  74.  
  75.     }else{  
  76.  
  77.         #子进程退出  
  78.  
  79.         logs("child quit\n");          
  80.  
  81.         exit 0;  
  82.  
  83.     }  
  84.  
  85. };  
  86.  
  87. $SIG{'INT'} = 'kill_pid'; # 中断退出  
  88.  
  89. $SIG{'TERM'} = 'kill_pid';  
  90.  
  91. $SIG{CHLD} = 'IGNORE'; # 忽略 SIGCHLD 信号,系统会自动回收结束的子进程  
  92.  
  93. #deamon方式  
  94.  
  95. daemonize();  
  96.  
  97. #启动服务  
  98.  
  99. logs("START deamon '$CMD'\n");  
  100.  
  101.  
  102.  
  103. while(1){  
  104.  
  105.     #防止输入的命令不是服务性命令,例如ls,执行很短时间的命令,或者后台执行的命令,这样最多会产生$maxloop个进程,不会把系统崩溃  
  106.  
  107.     $loop++;  
  108.  
  109.     if($loop>$maxloop){  
  110.  
  111.         exit 0;      
  112.  
  113.     }  
  114.  
  115.       
  116.  
  117.     #产生子进程  
  118.  
  119.     $child_pid=fork();  
  120.  
  121.     if (not defined $child_pid) {  
  122.  
  123.         print "cannot fork\n";  
  124.  
  125.         exit 0;  
  126.  
  127.     }  
  128.  
  129.     if($child_pid)  
  130.  
  131.     { # child >; 0, so we're the parent  
  132.  
  133.          $parent_pid=1;   
  134.  
  135.      logs("launching '$CMD'\n");   
  136.  
  137.      setpgrp(0, 0); #成为进程首领  
  138.  
  139.      wait();#等待子进程结束,若是结束则继续循环生成子进程  
  140.  
  141.     }else{   
  142.  
  143.             $parent_pid=0;  
  144.  
  145.             system($CMD);# child handles,子进程应该也是死循环的  
  146.  
  147.             logs("system return : $r ");  
  148.  
  149.      #执行命令非正常退出  
  150.  
  151.      if ($? == -1) {  
  152.  
  153.         logs("failed to execute: $! ");  
  154.  
  155.          }  
  156.  
  157.          elsif ($? & 127) {  
  158.  
  159.          logs("child died with signal",($? & 127),",",($? & 128) ? 'with' : 'without',' coredump');  
  160.  
  161.          }  
  162.  
  163.          else {  
  164.  
  165.          logs("child exited with value ",$? >> 8);  
  166.  
  167.              #若是$CMD正常退出的话,表示$CMD不是服务性程序  
  168.  
  169.                  logs("cannot deamon on '$CMD'");  
  170.  
  171.          }  
  172.  
  173.      #子进程退出      
  174.  
  175.      exit 0;  
  176.  
  177.     }   
  178.  
  179. }  
  180.  
  181.  
  182.  
  183. #记录日志  
  184.  
  185. sub logs{  
  186.  
  187.     open(LOGFILE,">>$logfile"or die("cannot open $logfile");  
  188.  
  189.         my($sec,$min,$hour,$mday,$mon,$year)=localtime();  
  190.  
  191.     print LOGFILE sprintf("%04d-%02d-%02d %02d:%02d:%02d ",($year< 2000?($year+1900):$year),($mon+1),$mday,$hour,$min,$sec);  
  192.  
  193.     for my $msg (@_){  
  194.  
  195.         print LOGFILE $msg;  
  196.  
  197.     }  
  198.  
  199.     print LOGFILE "\n";  
  200.  
  201.     close(LOGFILE);  
  202.  
  203. }  
  204.  
  205.  
  206.  
  207. #deamon方式  
  208.  
  209. sub daemonize {  
  210.  
  211.  
  212.  
  213.         #    使当前进程对自己所写文件拥有完全控制权,避免继承的umask()设置带来困挠。这一步可选  
  214.  
  215.         umask(0);  
  216.  
  217.         #    关闭0、1、2三个句柄。许多daemon程序用sysconf()获取_SC_OPEN_MAX,并在一个偱环中关闭所有可能打开的文件句柄。目的在于释放不必要的系统资源,它们是有限资源。  
  218.  
  219.         close STDIN;  
  220.  
  221.         close STDOUT;  
  222.  
  223.         close STDERR;  
  224.  
  225.     chdir '/' or croak "Can't chdir to /: $!"; #减少管理员卸载(unmount)文件系统时可能遇上的麻烦。这一步可选,也可chdir()到其它目录。  
  226.  
  227.     open STDIN, '/dev/null' or croak "Can't read /dev/null: $!";  
  228.  
  229.     open STDOUT, '>/dev/null' or croak "Can't write to /dev/null: $!";  
  230.  
  231.     open STDERR, '>&STDOUT' or croak "Can't dup stdout: $!";  
  232.  
  233.     defined(my $pid = fork) or croak "Can't fork: $!";  
  234.  
  235.     exit if $pid;  
  236.  
  237.     #创建新的session和process group,成为其leader,并脱离控制终端。  
  238.  
  239.     setsid or croak "Can't start a new session: $!";  
  240.  
  241.     $SIG{CHLD} = 'IGNORE 

 

         本文转自yifangyou 51CTO博客,原文链接:http://blog.51cto.com/yifangyou/607099,如需转载请自行联系原作者

上一篇:小技巧:tar命令打包目录时,排除文件和目录的命令


下一篇:nginx安装及配置支持php的教程(全)