之前做项目老大要求用写一服务,要求开机自启动,故研究了下shell脚本写服务的一些方法,感觉还是很不错的,写下来为了防止遗忘,码农不好干啊,记性越来越差了。
      首先写一个小程序,模拟常驻进程,简单起见,也就不写什么精灵进程了,就为了实验下。
  1. #include <stdio.h>
  2. #include <unistd.h>

  3. int main(int argc, char *argv[])
  4. {
  5.     while (1)
  6.     {
  7.         sleep(10);
  8.         printf("hello, world\n");
  9.     }
  10.     return 0;
  11. }
    然后写一个shell脚本作为来启动这个进程,可以随系统开机自启动,关机时结束运行,这里以CenotOS为例,Ubuntu好像有点不同。一般linux的服务都可以查看进程运行状态,开始运行和结束运行,重新启动。所以实现这三个函数就OK了。得说明下,按照约定,一般服务的名字都以d结尾。为了使用系统提供给我们的一些函数,可以包含/etc/init.d/functions这个文件,它给我们提供的有:检查进程运行状态,得到进程ID,结束进程等一些常用的函数。这个脚本一时半会还不容易看懂,有时间再研究下。
    首先是start函数,新建一个helld的文件,开头的固定格式为:
  1. # chkconfig: 2345 99 97

  2. if [ -f /etc/init.d/functions ] ; then
  3.     . /etc/init.d/functions
  4. elif [ -f /etc/init.d/functions ] ; then
  5.     . /etc/init.d/functions
  6. else
  7.     exit 1
  8. fi
其中# chkconfig: 2345 99 97中,2345代表该服务的运行级别(http://zh.wikipedia.org/wiki/%E8%BF%90%E8%A1%8C%E7%BA%A7%E5%88%AB),99和97代表卡机时启动的优先级和停止服务时的优先级,也就是启动顺序和停止顺序,数值从0-99,越大的优先级越低。

  1. $SERVICE_DIR="."
  2. $PROC_NAME="hello"
  3. start()
  4. {
  5.     for process in $PROC_NAME
  6.     do
  7.         if process_running $process
  8.         then
  9.             echo "$process is already running, exiting..."
  10.             echo_failure
  11.             exit_code=1
  12.         else
  13.             nohup $SERVICE_DIR/$process > /dev/null 2>&1 &
  14.             echo_success
  15.             echo
  16.         fi

  17.     done
  18. }
        其中的process_running如下:
  1. process_running()
  2. {
  3.     self_pid=$(pidofproc $1)
  4.     if [ -n "$self_pid" ] ; then
  5.         return 0
  6.     else
  7.         return 1
  8.     fi
  9. }
        这个process_running是检测进程是否存在,只允许运行一个实例的一种做法,其实是有很多中做法的。像用C/C++写的程序,可以直接运行的这种,这个process_running可能已经足够了。但是如果是像python这一类需要解释器来运行的程序就不行了,具体的做法是可以使用pidfile或者自己另写一个足以检测进程存在,在/etc/init.d/fuctions里面的status函数里有讲怎么用pidfile。
        然后是stop函数:

  1. stop()
  2. {
  3.     for process in $PROC_NAME
  4.     do
  5.         killproc $process
  6.         echo
  7.         RETVAL=$?
  8.         return $RETVAL
  9.     done

  10. }
        这其中用到了killproc这个函数,也是在/etc/init.d/functions里面实现的,具体的代码可以去参考下。
        最后是判断状态的函数,重起个名,叫process_status,避免冲突了

  1. process_status()
  2. {
  3.     for process in $PROC_NAME
  4.     do
  5.         if ! status $process
  6.         then
  7.             exit_code=1
  8.         else
  9.             echo_failure
  10.             exit_code=0
  11.         fi
  12.     done
  13. }
        这样呢,就把一个服务可能用到的都写出来了,接下来就是调用这些函数,

  1. case "$1" in
  2.     start)
  3.     start
  4.     ;;
  5.     stop)
  6.     stop;;
  7.     restart|reload)
  8.     stop
  9.     sleep 1
  10.     start
  11.     ;;
  12.     status)
  13.     process_status
  14.     ;;
  15.     *)
  16.     echo $"Usate: $0 {start|stop|restart|status}"
  17.     exit_code=1
  18.     ;;
  19. esac

  20. exit $exit_code
        注意到在最后有一句exit $exit_code,主要是为了向它的父进程传递退出代码,即$?。写完了这些呢,我们还需要让它实现开机启动,依次以root用户执行命令:

  1. mv hellod /etc/init.d
  2. /sbin/chkconfig --add hellod #通过chkconfig添加一个新的服务
  3. /sbin/chkconfig hellod on    #将这个服务加入开机自启动
        总算是大功告成了,很有成就感有木有,在满满的喜悦之后,俺也在感慨,现在写的这些简单的东西,实际上用到了其他人已经实现了的接口,真的是很感谢那些为linux付出的人。这么点东西一小会说完了,要用文字表达出来还是费了不少时间,惭愧啊。

 
10-08 06:59