• 在Linux下编写Daemon


    转自:http://blog.163.com/prevBlogPerma.do?host=manyhappy163&srl=1644768312010718111142260&mode=prev

    在Linux(以Redhat Linux Enterprise Edition 5.3为例)下,有时需要编写Service。Service也是程序,一般随系统启动用户不干预就不退出的程序,可以称为Service。Linux下的Service一般称为Daemon。

    以上是广义的Service的定义。Linux下的Service一般放在/etc/init.d文件夹下。浏览一下这个文件夹下的文件,可以发现在Linux下编写Service一般遵循的原则。

    一 Linux下编写Service一般遵循的原则
    1)真正运行的Service一般放在某个bin目录下(/bin,/usr/bin,etc)。
    2)/etc/init.d文件夹下一般是shell脚本,用来控制bin目录下的Service。
    3)/etc/init.d文件夹下的shell脚本一般接受至少两个参数,start和stop。还有其他常用的可选参数如status,reload,restart,等。
    4)/etc/init.d文件夹下的shell脚本至少包括两行注释,一行告诉chkconfig此服务运行的runlevel,启动优先级,结束优先级。一行告诉chkconfig此服务的描述。

    二 Linux的启动过程和RunLevel
    要理解Linux的启动过程和RunLevel,可以先浏览一下/etc/inittab文件。在/etc/inittab中定义了下面7种RunLevel。每个Service可以设置自己在哪个RunLevel下运行。可以调用/sbin/init <runlevel>进入相应的RunLevel,比如运行/sbin/init 6就会导致系统重启。如果在某个RunLevel下某个服务不能启动,导致系统启动失败,可以进入没有配置此服务的RunLevel来禁用或修改此服务(有点类似Windows下的安全模式)。

    #   0 - halt (Do NOT set initdefault to this)
    #   1 - Single user mode
    #   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
    #   3 - Full multiuser mode
    #   4 - unused
    #   5 - X11
    #   6 - reboot (Do NOT set initdefault to this)

    /etc/inittab文件下还定义了缺省RunLevel。如下,代表缺省RunLevel是5。
    id:5:initdefault:

    在/etc文件夹下,执行ls -d rc*,会列出下面这些文件和目录:
    rc  rc0.d  rc1.d  rc2.d  rc3.d  rc4.d  rc5.d  rc6.d  rc.d  rc.local  rc.sysinit
    rc是一个脚本,在/etc/inittab中,会根据RunLevel执行rc <runlevel>。rc脚本会到相应的rcN.d中去执行下面的脚本。rc.local是最后调用的脚本,可以放一些用户自定义的任务在里面。

    进入rcN.d文件夹下,会发现以S和K开头的脚本的链接,S和K后面还带2位数字。其中S代表Start,K代表Kill。S开头的脚本,在rc中调用的时候会带start参数;K开头的脚本,在rc中调用的时候会带stop参数。S和K后面带的2位数字代表Service的优先级,数字越大,越后执行。这些脚本的链接的目的地多半都在/etc/init.d文件夹下。

    现在一切都清晰了。我们可以通过在相应的rcN.d文件夹下按既定的规范创建/etc/init.d下脚本的软链接的方式来控制系统启动和退出时服务的启动和结束。但是用手动的方式创建软链接来管理毕竟不方便,RedHatLinux提供了chkconfig来帮助创建这些软链接。只要放在/etc/init.d下的服务控制脚本符合前面提到的chkconfig的约定(注释chkconfig 和 description),就可以用chkconfig --add <service>  chkconfig --list <service> chkconfig --del <service>等命令来控制service的启动与否。

    三 一个例子
    1)下面是用c++语言写的一个Service,此Service在/tmp/random文件中,每隔5秒生成一个4位随机数字。通过g++ -o myrand myrand.cpp编译。然后把myrand放到/root/bin/文件夹下。

    /* myrand.cpp
    * this program read 4 chars from /dev/random in each iteration,
    * and then adjust it to 0-9. Finally the 4 chars are written
    * to /tmp/random. This is only for testing /dev/random, and
    * at the same time serve as a example service.
    */
    #include <iostream>
    #include <fstream>
    using namespace std;
    #include <unistd.h>
    int main()
    {
    while (true)
        {
                        ifstream ifile("/dev/random");
    char ch;
    char str[5];
                        str[4]=0;
    int i;
    for(i=0;i<4;i++)
                        {
                                        ifile >> ch;
    if(ch<0) ch=-ch;
                                        ch = ch % 10;
                                        ch='0' + ch;
                                        str[i]=ch;
                        }
                        ofstream ofile("/tmp/random");
                        ofile << str << endl;
                        sleep(5);
        }
    }

    2) 下面是一个脚本,名字是myrandservice,放在/etc/init.d文件夹下:

    #!/bin/sh
    #
    # chkconfig: 2345  80 50
    # description: myrandservice is for testing how to write service in Linux 
    #              
    # processname: myrandservice

    # Source function library.
    . /etc/rc.d/init.d/functions
    ret=0
    start() {
    # check fdb status
                    echo "start myrandservice"
                    daemon /root/bin/myrand &
                    ret=$?

    stop() {
        echo "stop myrandservice"
    kill -9 $(ps -ef | grep myrand | grep -v grep | awk '{print $2}')
                    ret=$?

    status() {
    local result
                    echo "check status of myrandservice..."
    #lines=$( ps -ef | grep myrand | grep -v grep  |  )
                    #echo $lines
                    result=$( ps -ef | grep myrand | grep -v myrandservice | grep -v grep | wc -l )
    #echo $result
    if [ $result -gt 0 ] ; then
                                    echo "my randservice is up"
                                    ret=0
    else
                                    echo "my randservice is down"
                                    ret=1
                    fi
                    echo "check status of myrandservice...done."

    # See how we were called.
    case "$1" in
      start)
            start
            ;;
      stop)
            stop
            ;;
      status)
            status 
            ;;
    *)
            echo $"Usage: $0 {start|stop|status}"
    exit 1
    esac
    exit $ret

    3)使用/sbin/chkconfig --add myrandservice 来将次daemon设置为自动启动。 同时可以在rc3.d,rc4.d,rc5.d下面看到相应的脚本链接被自动地创建了。

    4)例子的一些说明
    例子中脚本的下面两行既是给chkconfig用的。其中2345代表此服务在RunLevel 2, 3, 4, 5下开启;80代表启动优先级;50代表结束优先级。如果RunLevel处不添值,用“-”代替,则代表此服务在任何runlevel下都不会自动启动,需要手动启动。可以通过service <service-name> start/stop/status等来控制或查询Service。

    # chkconfig: 2345  80 50
    # description: myrandservice is for testing how to write service in Linux 

    脚本中的daemon函数存在于/etc/rc.d/init.d/functions中。daemon会重定向输出到/dev/null,也会设置是否生成coredump文件。通过daemon启动的程序,即使用户退出了命令行shell,也会保证Service会运行而不会退出。在/etc/rc.d/init.d/functions中还包括其他一些有用的函数,如killproc,status等,分别用来杀掉服务和查看服务状态。

    四 deamon的另一种实现

    不使用/etc/rc.d/init.d/functions中的daemon,则Daemon程序设计要遵从以下过程:

    (1) 程序运行后调用fork,并让父进程退出。子进程获得一个新的进程ID,但继承了父进程的进程组ID。

    (2) 调用setsid创建一个新的session,使自己成为新session和新进程组的leader,并使进程没有控制终端(tty)。

    (3) 设置文件创建mask为0,避免创建文件时权限的影响。

    (4) 关闭不需要的打开文件描述符。因为Daemon程序在后台执行,不需要于终端交互,通常就关闭STDIN、STDOUT和STDERR。其它根据实际情况处理。

    (5) Daemon无法输出信息,可以使用SYSLOG或自己的日志系统进行日志处理。(可选)

    (6) 编写管理Daemon的SHELL脚本,使用service对Daemon进行管理和监控。(可选)

    参考:

    http://www.cnblogs.com/khler/archive/2011/01/30/1947971.html

    完!

    感谢,Thanks!
    作者:iTech
    出处:http://itech.cnblogs.com/
    本文版权归作者iTech所有,转载请包含作者签名和出处,不得用于商业用途,非则追究法律责任!

  • 相关阅读:
    SpringBoot的 CommandLineRunner的使用
    Lombok中关于@Data的使用解析
    @Autowired(required=false)注入注意的问题
    Java8新特性之StreamStream方法
    Java8新特性之Streamcollect方法
    Java 8 Stream.distinct() 列表去重示例
    Java开发之@PostConstruct和@PreConstruct注解
    并发基础篇(六):线程Thread类的start()方法和run()方法
    【MongoDB】MongoDB的复制(1)
    【PostgreSQL】PostgreSQL重建与主库不一致的从库
  • 原文地址:https://www.cnblogs.com/wuhen/p/2514741.html
Copyright © 2020-2023  润新知