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系统一般是不带图形界面的,所以基本上所有的软件都会提供一套在命令行运行的命令,有时候图形界面反应很慢,可以通过命令行控制软件,就是需要改变一下使用习惯。

  • 相关阅读:
    Atitit attilax要工作研究的要素 纪要 方案 趋势 方向 概念 理论
    Atitit 常见每日流程日程日常工作.docx v7 r8f
    Atitit it 互联网 软件牛人的博客列表
    Atitit 信息链(Information Chain)的概念理解 attilax总结
    Atitit 知识点的体系化 框架与方法 如何了解 看待xxx
    Atitit 聚合搜索多个微博 attilax总结
    Atitit 企业知识管理PKM与PIM
    Atitit 项目沟通管理 Atitit 沟通之道 attilax著.docx
    Atitit 项目管理软件 在线服务 attilax总结 1. 项目管理协作的历史 1 1.1. Worktile 406k 1 1.2. Teambition  584k in baidu
    Atitit.每周末总结 于每周一计划日程表 流程表 v8 import 上周遗漏日志补充 检查话费 检查流量情况 Crm问候 Crm表total and 问候
  • 原文地址:https://www.cnblogs.com/starRebel/p/6496900.html
Copyright © 2011-2022 走看看