linux service start|stop|restart


用了這么些日子的linux/unix系統,也和別人一起合作開發了不少程序,發現高手都喜歡在命令行上操作,而且控制程序的運行偏好於使用腳本,加上參數如:start、restart、stop等。

后來自己開發程序,也越來越覺得這樣是個好的方法:

1)節省時間,一鍵操作一系列步驟,需要記住的操作只有一兩個。

2)降低出錯概率,一次成功,次次成功。

3)提高通用性,同一套啟動腳本的代碼,可以被用在不同的程序上,需要修改的僅僅是待執行的程序命令。這也在另一個方面說明在命令行上操作程序的好處(其實每個linux程序歸根到底都得在命令行上執行)。

4)通過啟動腳本,可以做更多的控制,比如一次只運行一個程序實例,把輸出的信息重定向到日志文件中,查看狀態,結束進程等。

5)可以和別的命令結合使用。

 

具體而言,linux的系統服務大多通過start|stop這類方式操作。在目錄/etc/init.d中放着linux服務的啟動腳本,在安裝系統時,會把一些服務的啟動腳本放在這個目錄下。

同時,根據系統運行級別的不同,linux會運行/etc/rc$level.d/目錄下的啟動腳本。

http://www.360doc.com/content/12/0820/17/9336047_231349272.shtml

http://blog.csdn.net/acs713/article/details/7322082

有清楚的介紹。總結起來就是,系統啟動時,會根據運行級別,運行/etc/rc$level/下的腳本;而這些啟動腳本都是軟連接到/etc/init.d/目錄下的啟動腳本;也就是說/etc/init.d/里的腳本才是真正的啟動腳本,rc*.d/只是分了個類;所以,如果想要單獨操作某個服務,應當先到/etc/init.d/中去尋找。

 

在個人開發中,具體實踐起來是(我使用的是perl):

1. 創建一個配置文件daemon.conf。這個文件是用來記錄需要運行的命令,一行一個命令。

2. 創建一個startup.pl啟動腳本。腳本有三個參數

## get first argument
my $cmd = shift(@ARGV);
switch ($cmd) {
  case "start"   { start() }
  case "restart" { restart() }
  case "stop"    { stop() }
  else           { usage() }
}

3. 啟動之后,會創建一個文件daemon.proc記錄進程信息

my $PROCESS_FILE = "daemon.proc";
my $PROCESS_CONF = "daemon.conf";

如果是start命令,則首先判斷是不是已經在運行中,如果不是,則先創建daemon.proc文件,並加鎖。加鎖的目的是防止同時運行startup.pl文件。

flock函數是perl經常用來防止程序同時運行的方法,一般是先創建一個文件再加鎖,結束時對文件解鎖。不過這個加鎖對有的語言可能沒效果(比如java),原因待查明。。

if (-e $PROCESS_FILE) {
    print "fundamental services are running. Please stop them, then try again\n";
    exit 1;
  }
  my $fh;
  open ($fh, ">", $PROCESS_FILE) or die("unable to open $PROCESS_FILE");
  if (flock($fh,  LOCK_EX | LOCK_NB)) {
    ## we have the lock, launch services
    my $info = load();
    ## form json format
    my $jsonText = $json->pretty->encode($info);
    print $fh $jsonText;
    print "services started\n";
  } else {
    ## fail to get the lock, must be concurrency
    print "fail to get the lock of $PROCESS_FILE\n";
    exit 1;
  }

(linux的慣例是,在/var/lock/目錄下touch創建一個文件如/var/lock/subsys/httpd,用來表示已經有http實例在運行,這個文件主要是給其他進程看的)

(同時會在/var/run目錄下再創建一個文件/var/run/httpd/httpd.pid,記錄進程的pid,用於stop用)

(http://www.blogjava.net/jasmine214--love/archive/2010/06/25/324502.html)

(文件的應用方式不只是記錄信息)

/var/lock   
  鎖定文件.許多程序遵循在/var/lock 中產生一個鎖定文件的約定,以支持他們正在使用某個特定的設備或文件.其他程序注意到這個鎖定文件,將不試圖使用這個設備或文件.  

然后讀取要執行的命令

sub load {
  ## read cmds from config file
  my $cmds = readFile();
  ## collect process info
  my $info = [];
  foreach my $cmd (@$cmds) {
    push(@$info, launch($cmd));
  }
  return $info;
}

sub readFile {
  my $lines = [];
  open (my $fh, "<", $PROCESS_CONF) or die("unable to open $PROCESS_CONF");
  while (<$fh>) {
    chomp($_);
    if ($_ =~ m/^#/) {
      next;
    }
    ## use regular expression to extract arguments,
    ## especially those that are in Double quotation marks
    my $fields = removeQuote($_=~/\s*(\".*?\"|\S+)\s*/g);
    push (@$lines, $fields);
  }
  return $lines;
}

sub removeQuote {
  my @fields = @_;
  my $res = [];
  foreach my $field (@fields) {
    $field =~ s/\"//g;
    push(@$res, $field);
  }
  return $res;
}

在通過fork和exec來啟動程序

sub launch {
  my $cmd = shift;
  my $child = fork();
  if ($child > 0) {
    ## parent
    # sleep 1/4 second to ensure child is up
    select(undef, undef, undef, 0.25);
    my $info = {};
    $info->{pid} = $child;
    $info->{cmd} = $cmd;
    return $info;
  } else {
    ## child process
    ## set as deamon process
    if (!setsid()) {
      print "unable to setsid()\n";
      exit 1;
    }
    ## redirect STDERR and STDOUT to /dev/null,
   ## we can also redirect them to ./log open (STDOUT, ">/dev/null"); open (STDERR, ">&STDOUT"); eval { exec(@$cmd); }; print "Faied to exec() cmd:".Dumper($cmd)." $@"; exit 1; } }

運行前:daemon.conf

./count.pl

運行后:daemon.proc

[
   {
      "cmd" : [
         "./count.pl"
      ],
      "pid" : 4665
   }
]

如果是stop命令,則直接讀取deamon.proc,殺死相應進程

sub stop {
  if (!-e $PROCESS_FILE) {
    print "no services are running\n";
    exit 1;
  }
  killProcess();
}

sub killProcess {
  my $infoArray = Utils::SystemCalls->readJsonFile($PROCESS_FILE);
  foreach my $info (@$infoArray) {
    print "kill $info->{pid}\n";
    print `kill -15 $info->{pid}`; # send SIGTERM
  }
  print `rm $PROCESS_FILE`;
}

kill命令會發送一個信號給目標進程, 信號使監視與控制其他進程變為有可能。對接收進程來說,如果沒有設置信號處理函數,那么在接收信號后,會執行默認操作;接收進程也可以攔截信號,自行處理。參考http://www.freeoa.net/development/perl/the-signal-under-perl_2671.html

 

重復多次的操作,最好腳本化。perl腳本實質上也是linux bash命令的組合,只是多了一些包裝和日志,這樣在命令出錯時,可以知道是哪個命令出錯了,並停止在出錯的命令處;也可以結合perl和shell兩邊的優點。腳本語言的選擇可以從熟練度出發。shell熟練就是用shell腳本。

習慣使用命令行的好處是,linux自帶大量優秀的程序,在終端上調用這些程序十分方便,而這些程序通過管道組合起來的威力更是十分強大,可以輕松地幫我們解決問題。兼具操作的簡便和運行的效率。

服務器上的linux系統一般是不帶圖形界面的,所以基本上所有的軟件都會提供一套在命令行運行的命令,有時候圖形界面反應很慢,可以通過命令行控制軟件,就是需要改變一下使用習慣。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM