如何使用python守护进程设置守护进程?

如何使用python守护进程设置守护进程?,python,daemon,python-daemon,Python,Daemon,Python Daemon,我是新来的守护进程,所以如果这是一个新手问题,我深表歉意 在其他几个答案中(例如),人们认为该软件包是可行的,因为它完全实现了标准 不幸的是,python守护进程是(或者更可能的是我对知识/经验有点了解…;),我想我可能缺少一些真正基本的东西。以下是我正在做的: 我有以下资料: import daemon logfile = open('daemon.log', 'w') context = daemon.DaemonContext(stdout = logfile, stderr = lo

我是新来的守护进程,所以如果这是一个新手问题,我深表歉意

在其他几个答案中(例如),人们认为该软件包是可行的,因为它完全实现了标准

不幸的是,python守护进程是(或者更可能的是我对知识/经验有点了解…;),我想我可能缺少一些真正基本的东西。以下是我正在做的:

我有以下资料:

import daemon

logfile = open('daemon.log', 'w')

context = daemon.DaemonContext(stdout = logfile, stderr = logfile)

context.open()

with context:
    do_something_1()
    do_something_2()
问题:如何使用python守护程序设置守护程序,如何启动和停止它?


旁注:

我基本上是在猜测这里应该如何/是否使用
.open()
方法——文档在这一点上并不十分清楚。不管我是否包括在内,同样的事情似乎也会发生

那么,现在我该怎么办?当我尝试运行此文件时,例如:

python startConsumerDaemons.py
它似乎在运行
do\u something\u 1()
,但不是第二个。而且,它似乎将程序附加到终端窗口。也就是说,stdout没有重定向,当我关闭终端窗口时,进程被终止。所以,我很确定我做错了什么。。。我应该做些什么不同的事情


最后,在运行守护进程后,如何停止/重新启动它(例如,如果我更改了底层代码)?

在linux上,您可以通过运行以下命令来停止守护进程:

$ ps -x
找到与守护进程对应的PID,然后终止进程。

下面是一个完整的示例

您应该能够更好地理解python守护进程的内部工作原理

此外,提供的代码还提供了一个init脚本示例,用于简单地启动/停止守护进程。但是,您可以通过使用参数stop再次调用原始函数来启动/停止它:

python original_func.py stop
正如您在中所看到的,it语句确实执行了一些“魔术”,这与我们的目的有关。具体而言:

执行带有一个“项”的with语句的过程如下 如下:

  • 对上下文表达式(with_项中给出的表达式)求值以获得上下文管理器

  • 将加载上下文管理器的
    \uuuuu exit\uuuu()
    ,以供以后使用

  • 调用上下文管理器的
    \uuuu enter\uuuu()
    方法

  • 如果with语句中包含目标,则将
    \uuuu enter\uuuu()
    中的返回值分配给它

  • 该套件已执行

  • 将调用上下文管理器的
    \uuuu exit\uuu()
    方法。如果异常导致套件退出,则其类型、值和 回溯作为参数传递给
    \uuuu exit\uuu()
    。否则,三无 提供了参数

  • 这是什么意思?如果您仔细查看,它也可以作为python守护程序文档(而且确实可以改进很多),您将看到它实现了
    \uuuuuu enter\uuuuuu()
    \uuuu exit\uuuuu()

    该类还通过
    \uuuuu enter\uuuuu
    实现上下文管理器协议 和
    \uuuu退出\uuuu
    方法

    \uuuuuuuuuuuuuuuuuuuuuuuuuuuu()

    调用实例的open()方法,然后返回实例

    \uuuu退出(exc\u类型、exc\u值、exc\u回溯)

    调用实例的close()方法,如果异常已处理,则返回True;如果未处理,则返回False

    换句话说,不需要open(),PEP中给出的示例(尽管没有正确解释)可以按原样工作。虽然with语句确实有意义,但它不保留任何循环,一旦到达其作用域的末尾,它将调用exit(),这在python守护进程中表示close()。因此,你需要把它放在一段时间内,或者你考虑的是无限循环。 关于你的第二个剧本不起作用,我真的不能告诉你,我很惊讶第一个剧本已经起作用了。如果守护进程正在停止,那么脚本肯定有问题,您可以检查consumerDaemonLogFile。(作为旁注,您有一个输入错误'sderr'-->'stderr')

    此外,您可以在PEP中看到,如果未指定,working directory属性默认为“/”。如果在脚本中使用相对路径,这可能是问题的根源

    最后,关于最后一个问题,如果你发现它的PID,你可以很容易地杀死它:

    ps ax | grep startConsumerDaemons.py
    
    并发送一个SIGTERM:

    kill <pid>
    
    杀死
    gromain提供的答案确实提供了一种更方便的启动和停止方法,即使用“daemon.runner()”,但安装起来要复杂得多

    这是我所拥有的,对我有用。它还有一个sysv init脚本,我还提供了我找到的其他可能解决方案的链接

    只能有一个守护进程在运行:与大多数其他Linux守护进程一样,该进程由PID锁文件管理。要阻止它,就做吧

    kill `cat /var/run/eg_daemon.pid`
    
    要查看它是否正在运行,请执行以下操作:

    ps -elf | grep `cat /var/run/eg_daemon.pid`
    
    使用PID文件子模块,PID文件将自动管理。当守护进程停止时,pidfile将被清除。有关初始化脚本,请参阅链接的GitHub repo

    下面是Python守护程序代码:

    #!/usr/bin/env python3.5
    import sys
    import os
    import time
    import argparse
    import logging
    import daemon
    from daemon import pidfile
    
    debug_p = False
    
    def do_something(logf):
        ### This does the "work" of the daemon
    
        logger = logging.getLogger('eg_daemon')
        logger.setLevel(logging.INFO)
    
        fh = logging.FileHandler(logf)
        fh.setLevel(logging.INFO)
    
        formatstr = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        formatter = logging.Formatter(formatstr)
    
        fh.setFormatter(formatter)
    
        logger.addHandler(fh)
    
        while True:
            logger.debug("this is a DEBUG message")
            logger.info("this is an INFO message")
            logger.error("this is an ERROR message")
            time.sleep(5)
    
    
    def start_daemon(pidf, logf):
        ### This launches the daemon in its context
    
        ### XXX pidfile is a context
        with daemon.DaemonContext(
            working_directory='/var/lib/eg_daemon',
            umask=0o002,
            pidfile=pidfile.TimeoutPIDLockFile(pidf),
            ) as context:
            do_something(logf)
    
    
    if __name__ == "__main__":
        parser = argparse.ArgumentParser(description="Example daemon in Python")
        parser.add_argument('-p', '--pid-file', default='/var/run/eg_daemon.pid')
        parser.add_argument('-l', '--log-file', default='/var/log/eg_daemon.log')
    
        args = parser.parse_args()
    
        start_daemon(pidf=args.pid_file, logf=args.log_file)
    
    为了完整起见,这里是init脚本。请注意,“kill”实际上只是发送POSIX信号的一种方法——有关概述,请参阅信号手册页(7)。python守护进程上下文将捕获该信号,干净地结束关闭文件描述符的进程,并自动删除PID文件。所以,这真的是一个干净的终止

    您可以编写代码来捕获SIGUSR1或类似的内容,以便重新加载守护程序配置。编写Python停止守护进程没有任何好处

    #!/bin/bash
    #
    # eg_daemon      Startup script for eg_daemon
    #
    # chkconfig: - 87 12
    # description: eg_daemon is a dummy Python-based daemon
    # config: /etc/eg_daemon/eg_daemon.conf
    # config: /etc/sysconfig/eg_daemon
    # pidfile: /var/run/eg_daemon.pid
    #
    ### BEGIN INIT INFO
    # Provides: eg_daemon
    # Required-Start: $local_fs
    # Required-Stop: $local_fs
    # Short-Description: start and stop eg_daemon server
    # Description: eg_daemon is a dummy Python-based daemon
    ### END INIT INFO
    
    # Source function library.
    . /etc/rc.d/init.d/functions
    
    if [ -f /etc/sysconfig/eg_daemon ]; then
            . /etc/sysconfig/eg_daemon
    fi
    
    eg_daemon=/var/lib/eg_daemon/eg_daemon.py
    prog=eg_daemon
    pidfile=${PIDFILE-/var/run/eg_daemon.pid}
    logfile=${LOGFILE-/var/log/eg_daemon.log}
    RETVAL=0
    
    OPTIONS=""
    
    start() {
            echo -n $"Starting $prog: "
    
            if [[ -f ${pidfile} ]] ; then
                pid=$( cat $pidfile  )
                isrunning=$( ps -elf | grep  $pid | grep $prog | grep -v grep )
    
                if [[ -n ${isrunning} ]] ; then
                    echo $"$prog already running"
                    return 0
                fi
            fi
            $eg_daemon -p $pidfile -l $logfile $OPTIONS
            RETVAL=$?
            [ $RETVAL = 0 ] && success || failure
            echo
            return $RETVAL
    }
    
    stop() {
        if [[ -f ${pidfile} ]] ; then
            pid=$( cat $pidfile )
            isrunning=$( ps -elf | grep $pid | grep $prog | grep -v grep | awk '{print $4}' )
    
            if [[ ${isrunning} -eq ${pid} ]] ; then
                echo -n $"Stopping $prog: "
                kill $pid
            else
                echo -n $"Stopping $prog: "
                success
            fi
            RETVAL=$?
        fi
        echo
        return $RETVAL
    }
    
    reload() {
        echo -n $"Reloading $prog: "
        echo
    }
    
    # See how we were called.
    case "$1" in
      start)
        start
        ;;
      stop)
        stop
        ;;
      status)
        status -p $pidfile $eg_daemon
        RETVAL=$?
        ;;
      restart)
        stop
        start
        ;;
      force-reload|reload)
        reload
        ;;
      *)
        echo $"Usage: $prog {start|stop|restart|force-reload|reload|status}"
        RETVAL=2
    esac
    
    exit $RETVAL
    

    模块“python守护进程”仍然缺少有用的文档。我个人放弃了使用它,现在我成功地使用了Sander Marechal的守护程序代码

    我稍微修改了它,以便能够在调用
    python testdaemon.py stop
    时执行操作。编辑2020/05/07:修改和
    import sys, daemon, time
    
    class testdaemon(daemon.Daemon):
        def run(self):
            self.i = 0
            with open('test1.txt', 'w') as f:
                f.write(str(self.i))
            while True:
                self.i += 1
                time.sleep(1)
    
        def quit(self):
            with open('test2.txt', 'w') as f:
                f.write(str(self.i))
    
    daemon = testdaemon()
    
    if 'start' == sys.argv[1]: 
        daemon.start()
    elif 'stop' == sys.argv[1]: 
        daemon.stop()
    elif 'restart' == sys.argv[1]: 
        daemon.restart()
    
    import os, sys
    import datetime
    import time
    import daemon
    import daemon.pidfile
    import argparse
    import signal
    import logging
    
    PROGNAME = 'monitor'
    PATHCTRL = '/tmp/' #path to control files pid and lock
    pidpath = os.path.join(PATHCTRL,  PROGNAME + ".pid")
    parser = argparse.ArgumentParser(prog = PROGNAME)
    
    sp = parser.add_subparsers()
    sp_start = sp.add_parser('start', help='Starts %(prog)s daemon')
    sp_stop = sp.add_parser('stop', help='Stops %(prog)s daemon')
    sp_status = sp.add_parser('status', help='Show the status of %(prog)s daemon')
    sp_restart = sp.add_parser('restart', help='Restarts %(prog)s daemon')
    sp_debug = sp.add_parser('debug', help='Starts %(prog)s daemon in debug mode')
    sp_start.add_argument('-v', '--verbose', action='store_true', help='log extra informations')
    sp_debug.add_argument('-v', '--verbose', action='store_true', help='log extra informations')
    
    class MainCtrl:
      thread_continue = True
      thread_token = "token"
    
    mainctrl = MainCtrl()
    
    def main_thread_stop(signum=None, frame=None):
        mainctrl.thread_continue = False
        mainctrl.thread_token = "test"
     
    def main_thread(args, mainctrl):
        verbose = False
        if hasattr(args, 'mainctrl'):
          verbose = args.verbose
        if verbose:
          print("MAIN:{0}".format(args))
        try:
          while mainctrl.thread_continue:
             if verbose:
                print("TOKEN:{0}".format(mainctrl.thread_token))
             time.sleep(1)
        except KeyboardInterrupt as ke:
          if verbose:
            print("INFO: Existing...") 
        except Exception as e:
          if verbose:
            print("ERROR: Exception:{0}".format(str(e)))    
    
    def daemon_start(args=None):
        print("INFO: Daemon Start")
        if os.path.exists(pidpath):
          print("INFO: Daemon already running (according to {0}).".format(pidpath))
          sys.exit(1)
        else:
          with daemon.DaemonContext(
            stdout=sys.stdout,
            stderr=sys.stderr,
            signal_map={
                signal.SIGTERM: main_thread_stop,
                signal.SIGTSTP: main_thread_stop,
                signal.SIGINT: main_thread_stop,
                #signal.SIGKILL: daemon_stop, #SIGKILL is an Invalid argument
                signal.SIGUSR1: daemon_status,
                signal.SIGUSR2: daemon_status,
              },
              pidfile = daemon.pidfile.PIDLockFile(pidpath)
              ):
            print("INFO: Running daemon...")
            main_thread(args, mainctrl)
    
    def daemon_restart(args):
        print("INFO: Daemon Restart")
        daemon_stop()
        time.sleep(1)
        daemon_start(args)
    
    def daemon_stop(args=None):
        print("INFO: Daemon Stop {0}".format(args))
        if os.path.exists(pidpath):
          with open(pidpath) as pid:
            try:
              os.kill(int(pid.readline()), signal.SIGINT)
            except ProcessLookupError as ple:
              os.remove(pidpath)
              print("ERROR: {0}".format(ple))
        else:
          print("INFO: process isn't running (according to the absence of {0}).".format(pidpath))
    
    def daemon_debug(args):
        print("INFO: Daemon debug")
        main_thread(args, mainctrl)
    
    def daemon_status(args):
        print("INFO: Daemon Status")
        if os.path.exists(pidpath):
          print("INFO: Daemon is running")
        else:
          print("INFO: Daemon is not running.")
    
    sp_stop.set_defaults(callback=daemon_stop)
    sp_status.set_defaults(callback=daemon_status)
    sp_start.set_defaults(callback=daemon_start)
    sp_restart.set_defaults(callback=daemon_restart)
    sp_debug.set_defaults(callback=daemon_debug)
    
    args = parser.parse_args()
    
    if hasattr(args, 'callback'):
      args.callback(args)
    else:
      parser.print_help()