天天看點

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[0];  

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");          

    }  

};  

$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";  

    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("%04d-%02d-%02d %02d:%02d:%02d ",($year< 2000?($year+1900):$year),($mon+1),$mday,$hour,$min,$sec);  

    for my $msg (@_){  

        print LOGFILE $msg;  

    print LOGFILE "\n";  

    close(LOGFILE);  

sub daemonize {  

        #    使目前程序對自己所寫檔案擁有完全控制權,避免繼承的umask()設定帶來困撓。這一步可選  

        umask(0);  

        #    關閉0、1、2三個句柄。許多daemon程式用sysconf()擷取_SC_OPEN_MAX,并在一個偱環中關閉所有可能打開的檔案句柄。目的在于釋放不必要的系統資源,它們是有限資源。  

        close STDIN;  

        close STDOUT;  

        close STDERR;  

    chdir '/' or croak "Can't chdir to /: $!"; #減少管理者解除安裝(unmount)檔案系統時可能遇上的麻煩。這一步可選,也可chdir()到其它目錄。  

    open STDIN, '/dev/null' or croak "Can't read /dev/null: $!";  

    open STDOUT, '>/dev/null' or croak "Can't write to /dev/null: $!";  

    open STDERR, '>&STDOUT' or croak "Can't dup stdout: $!";  

    defined(my $pid = fork) or croak "Can't fork: $!";  

    exit if $pid;  

    #建立新的session和process group,成為其leader,并脫離控制終端。  

    setsid or croak "Can't start a new session: $!";  

    $SIG{CHLD} = 'IGNORE 

         本文轉自yifangyou 51CTO部落格,原文連結:http://blog.51cto.com/yifangyou/607099,如需轉載請自行聯系原作者

繼續閱讀