運維需要對一些關鍵的服務程序進行守護,例如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,如需轉載請自行聯系原作者