tubaobaoya3 发表于 2018-8-31 13:34:08

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

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

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


[*]#!/usr/bin/perl
[*]
[*]####################################################
[*]
[*]#功能:实现把传入的命令执行,并守护,当命令被杀死了,能够重新启动命令
[*]
[*]#系统环境:centos 5
[*]
[*]#编译环境:perl, v5.8.8 built for i386-linux-thread-multi
[*]
[*]#执行:
[*]
[*]#echo "i=0;" > /root/w.sh
[*]
[*]#echo 'while(true)' >> /root/w.sh
[*]
[*]#echo "do" >> /root/w.sh
[*]
[*]#echo ' echo $i;let i=$i+1;sleep 1;' >> /root/w.sh
[*]
[*]#echo 'done' >> /root/w.sh
[*]
[*]#perl deamon.pl "sh /root/w.sh >> /root/w.log"
[*]
[*]#
[*]
[*]####################################################
[*]
[*]use POSIX ();
[*]
[*]use Carp;
[*]
[*]
[*]
[*]our $logfile="/tmp/deamon.log"; #输出日志
[*]
[*]our $child_pid; #记录子进程的id,以便父进程去杀死子进程
[*]
[*]our $parent_pid;#记录父进程的id,以便区分父子进程
[*]
[*]our $maxloop=10000; #放置大量产生子进程
[*]
[*]our $loop=0;
[*]
[*]our $CMD=$ARGV;
[*]
[*]if(!$CMD){
[*]
[*]    die("please input a cmd\n");
[*]
[*]}
[*]
[*]
[*]
[*]#杀死父子进程
[*]
[*]sub kill_pid{
[*]
[*]    logs("catch quit signal\n");
[*]
[*]    if($parent_pid){#判断是不是父进程
[*]
[*]      #父进程杀死所有的子进程
[*]
[*]      logs("parent kill child\n");
[*]
[*]      kill(15,$child_pid);
[*]
[*]      kill(15,-$$);
[*]
[*]      logs("parent quit\n");
[*]
[*]      exit 0;
[*]
[*]    }else{
[*]
[*]      #子进程退出
[*]
[*]      logs("child quit\n");
[*]
[*]      exit 0;
[*]
[*]    }
[*]
[*]};
[*]
[*]$SIG{'INT'} = 'kill_pid'; # 中断退出
[*]
[*]$SIG{'TERM'} = 'kill_pid';
[*]
[*]$SIG{CHLD} = 'IGNORE'; # 忽略 SIGCHLD 信号,系统会自动回收结束的子进程
[*]
[*]#deamon方式
[*]
[*]daemonize();
[*]
[*]#启动服务
[*]
[*]logs("START deamon '$CMD'\n");
[*]
[*]
[*]
[*]while(1){
[*]
[*]    #防止输入的命令不是服务性命令,例如ls,执行很短时间的命令,或者后台执行的命令,这样最多会产生$maxloop个进程,不会把系统崩溃
[*]
[*]    $loop++;
[*]
[*]    if($loop>$maxloop){
[*]
[*]      exit 0;
[*]
[*]    }
[*]
[*]
[*]
[*]    #产生子进程
[*]
[*]    $child_pid=fork();
[*]
[*]    if (not defined $child_pid) {
[*]
[*]      print "cannot fork\n";
[*]
[*]      exit 0;
[*]
[*]    }
[*]
[*]    if($child_pid)
[*]
[*]    { # child >; 0, so we're the parent
[*]
[*]         $parent_pid=1;
[*]
[*]   logs("launching '$CMD'\n");
[*]
[*]   setpgrp(0, 0); #成为进程首领
[*]
[*]   wait();#等待子进程结束,若是结束则继续循环生成子进程
[*]
[*]    }else{
[*]
[*]            $parent_pid=0;
[*]
[*]            system($CMD);# child handles,子进程应该也是死循环的
[*]
[*]            logs("system return : $r ");
[*]
[*]   #执行命令非正常退出
[*]
[*]   if ($? == -1) {
[*]
[*]      logs("failed to execute: $! ");
[*]
[*]         }
[*]
[*]         elsif ($? & 127) {
[*]
[*]         logs("child died with signal",($? & 127),",",($? & 128) ? 'with' : 'without',' coredump');
[*]
[*]         }
[*]
[*]         else {
[*]
[*]         logs("child exited with value ",$? >> 8);
[*]
[*]             #若是$CMD正常退出的话,表示$CMD不是服务性程序
[*]
[*]               logs("cannot deamon on '$CMD'");
[*]
[*]         }
[*]
[*]   #子进程退出
[*]
[*]   exit 0;
[*]
[*]    }
[*]
[*]}
[*]
[*]
[*]
[*]#记录日志
[*]
[*]sub logs{
[*]
[*]    open(LOGFILE,">>$logfile") or die("cannot open $logfile");
[*]
[*]      my($sec,$min,$hour,$mday,$mon,$year)=localtime();
[*]
[*]    print LOGFILE sprintf(&quot;%04d-%02d-%02d %02d:%02d:%02d &quot;,($year< 2000?($year+1900):$year),($mon+1),$mday,$hour,$min,$sec);
[*]
[*]    for my $msg (@_){
[*]
[*]      print LOGFILE $msg;
[*]
[*]    }
[*]
[*]    print LOGFILE &quot;\n&quot;;
[*]
[*]    close(LOGFILE);
[*]
[*]}
[*]
[*]
[*]
[*]#deamon方式
[*]
[*]sub daemonize {
[*]
[*]
[*]
[*]      #    使当前进程对自己所写文件拥有完全控制权,避免继承的umask()设置带来困挠。这一步可选
[*]
[*]      umask(0);
[*]
[*]      #    关闭0、1、2三个句柄。许多daemon程序用sysconf()获取_SC_OPEN_MAX,并在一个偱环中关闭所有可能打开的文件句柄。目的在于释放不必要的系统资源,它们是有限资源。
[*]
[*]      close STDIN;
[*]
[*]      close STDOUT;
[*]
[*]      close STDERR;
[*]
[*]    chdir '/' or croak &quot;Can't chdir to /: $!&quot;; #减少管理员卸载(unmount)文件系统时可能遇上的麻烦。这一步可选,也可chdir()到其它目录。
[*]
[*]    open STDIN, '/dev/null' or croak &quot;Can't read /dev/null: $!&quot;;
[*]
[*]    open STDOUT, '>/dev/null' or croak &quot;Can't write to /dev/null: $!&quot;;
[*]
[*]    open STDERR, '>&STDOUT' or croak &quot;Can't dup stdout: $!&quot;;
[*]
[*]    defined(my $pid = fork) or croak &quot;Can't fork: $!&quot;;
[*]
[*]    exit if $pid;
[*]
[*]    #创建新的session和process group,成为其leader,并脱离控制终端。
[*]
[*]    setsid or croak &quot;Can't start a new session: $!&quot;;
[*]
[*]    $SIG{CHLD} = 'IGNORE
  



页: [1]
查看完整版本: perl实现对各种命令的守护进程,能够自动重启进程