zoukankan      html  css  js  c++  java
  • 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
    ";
        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
    ";
      } else {
        ## fail to get the lock, must be concurrency
        print "fail to get the lock of $PROCESS_FILE
    ";
        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()
    ";
          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
    ";
        exit 1;
      }
      killProcess();
    }
    
    sub killProcess {
      my $infoArray = Utils::SystemCalls->readJsonFile($PROCESS_FILE);
      foreach my $info (@$infoArray) {
        print "kill $info->{pid}
    ";
        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系统一般是不带图形界面的,所以基本上所有的软件都会提供一套在命令行运行的命令,有时候图形界面反应很慢,可以通过命令行控制软件,就是需要改变一下使用习惯。

  • 相关阅读:
    如何更改 iOS 和安卓浏览器上的 input[type="radio"] 元素的默认样式?
    js获取样式、currentStyle和getComputedStyle的兼容写法
    js选中文字兼容性解决
    最短的IE判断var ie=!-[1,]分析
    总结oninput、onchange与onpropertychange事件的用法和区别
    jQuery工具函数
    Vnpy官网汇总
    Anaconda下各个Python版本下载地址
    APScheduler——定时任务框架
    PyQt5——隐藏控件并保留位置
  • 原文地址:https://www.cnblogs.com/starRebel/p/6496900.html
Copyright © 2011-2022 走看看