40

私はデーモンが初めてなので、これが初心者の質問である場合は申し訳ありません。

他のいくつかの回答 (たとえば、この質問)では、 PEP 3143標準を完全に実装しているため、 python-daemonパッケージを使用することをお勧めします。

残念ながら、python-daemon はドキュメンテーションが少し不足しています(または、知識/経験が少し不足している可能性が高いです... ;))、おそらく本当に基本的なものが欠けていると思います。これが私がやっていることです:

私は次のものを持っています:

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-daemon を使用してデーモンをセットアップするにはどうすればよいですか? また、デーモンを開始および停止するにはどうすればよいですか?


補足:

私は基本的に、.open()ここでメソッドをどのように使用するか/使用する必要があるかについて、大雑把な推測をしています-ドキュメントはこの点について明確ではありませんでした。含めても含めなくても同じことが起こるようです。

それで、今私は何をしますか?このファイルを実行しようとすると、たとえば次のようになります。

python startConsumerDaemons.py

実行されているように見えますdo_something_1()が、2番目ではありません。また、ターミナル ウィンドウにプログラムが接続されたままになっているように見えます。IE、stdout はリダイレクトされず、ターミナル ウィンドウを閉じるとプロセスが強制終了されます。だから、私はここで何か間違ったことをしていると確信しています...私は別のことをすべきですか?

最後に、デーモンを実行した後、デーモンを停止/再起動するにはどうすればよいですか (たとえば、基になるコードを変更した場合)。

4

7 に答える 7

18

ここに私が持っているものがあります。sysv init スクリプトもあります。Repo は GitHubにあります。また、私が見つけた他の解決策へのリンクを含む簡単なブログ投稿もあります。

実行できるデーモン プロセスは 1 つだけです。これは、他のほとんどの Linux デーモンと同様に、PID ロック ファイルによって管理されます。それを止めるには、

kill `cat /var/run/eg_daemon.pid`

実行中かどうかを確認するには:

ps -elf | grep `cat /var/run/eg_daemon.pid`

pidfile サブモジュールを使用すると、PID ファイルは自動的に管理されます。デーモンが停止すると、pidfile はクリアされます。init スクリプトについては、リンクされた GitHub リポジトリを参照してください。

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 シグナルを送信するための単なる方法であることに注意してください。概要については、signal(7) のマニュアル ページを参照してください。python-daemon コンテキストはシグナルをキャッチし、ファイル記述子を完全に閉じてプロセスを終了し、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
于 2016-11-10T20:04:33.010 に答える
6

完全な例はここにあります

python-daemonの内部動作をよりよく理解できるはずです。

さらに、提供されているコードは、デーモンを単純に開始/停止するためのinitスクリプトの例も示しています。ただし、引数stopを指定して元の関数を再度呼び出すだけで、開始/停止できます。

python original_func.py stop
于 2013-02-25T18:21:18.353 に答える
3

これは、argparse とシグナル処理を備えた 2020 python-daemon ボイラープレートです。ログ付きのバージョンについては、こちらをご覧ください

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()
于 2020-08-06T01:07:54.093 に答える
2

モジュール「python-daemon」に関する有用なドキュメントがまだありません。私は個人的に使用をあきらめましたが、この回答で参照されている Sander Marechal のデーモン コードを正常に使用しています。

を呼び出したときにできるように、少し修正しましたpython testdaemon.py stop。編集 2020/05/07: Python 3 を変更して使用します。

ここにコードがあります


使用例:

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()
于 2016-11-12T10:44:59.450 に答える
0

Linux では、次のコマンドを実行してデーモンを停止できます。

$ ps -x

デーモンに対応するPIDを見つけて、プロセスを強制終了します。

于 2012-12-19T00:05:58.720 に答える