如何让 Python 脚本像 Linux 中的服务或守护进程一样运行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1603109/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:48:18  来源:igfitidea点击:

How to make a Python script run like a service or daemon in Linux

pythonlinuxscriptingdaemons

提问by adhanlon

I have written a Python script that checks a certain e-mail address and passes new e-mails to an external program. How can I get this script to execute 24/7, such as turning it into daemon or service in Linux. Would I also need a loop that never ends in the program, or can it be done by just having the code re executed multiple times?

我编写了一个 Python 脚本,用于检查某个电子邮件地址并将新电子邮件传递给外部程序。我怎样才能让这个脚本 24/7 全天候执行,比如在 Linux 中将它变成守护程序或服务。我是否还需要一个永远不会在程序中结束的循环,还是可以通过多次重新执行代码来完成?

采纳答案by P Shved

You have two options here.

您在这里有两个选择。

  1. Make a proper cron jobthat calls your script. Cron is a common name for a GNU/Linux daemon that periodically launches scripts according to a schedule you set. You add your script into a crontab or place a symlink to it into a special directory and the daemon handles the job of launching it in the background. You can read moreat Wikipedia. There is a variety of different cron daemons, but your GNU/Linux system should have it already installed.

  2. Use some kind of python approach(a library, for example) for your script to be able to daemonize itself. Yes, it will require a simple event loop (where your events are timer triggering, possibly, provided by sleep function).

  1. 制作一个适当的cron 作业来调用您的脚本。Cron 是 GNU/Linux 守护进程的通用名称,它根据您设置的时间表定期启动脚本。您将脚本添加到 crontab 或将它的符号链接放入一个特殊目录,守护进程处理在后台启动它的工作。您可以在 Wikipedia 上阅读更多内容。有多种不同的 cron 守护进程,但您的 GNU/Linux 系统应该已经安装了它。

  2. 使用某种python 方法(例如一个库)让你的脚本能够自我守护。是的,它需要一个简单的事件循环(其中您的事件是定时器触发,可能由睡眠功能提供)。

I wouldn't recommend you to choose 2., because you would be, in fact, repeating cron functionality. The Linux system paradigm is to let multiple simple tools interact and solve your problems. Unless there are additional reasons why you should make a daemon (in addition to trigger periodically), choose the other approach.

我不建议您选择 2.,因为实际上您会重复 cron 功能。Linux 系统范式是让多个简单的工具交互并解决您的问题。除非有其他原因需要创建守护程序(除了定期触发),否则请选择其他方法。

Also, if you use daemonize with a loop and a crash happens, no one will check the mail after that (as pointed out by Ivan Nevostruevin comments to thisanswer). While if the script is added as a cron job, it will just trigger again.

此外,如果您在循环中使用 daemonize 并且发生崩溃,则此后没有人会检查邮件(正如Ivan Nevostruev对此答案的评论中所指出的那样)。而如果将脚本添加为 cron 作业,它只会再次触发。

回答by Prody

You should use the python-daemonlibrary, it takes care of everything.

您应该使用python-daemon库,它会处理一切。

From PyPI: Library to implement a well-behaved Unix daemon process.

来自 PyPI:用于实现行为良好的 Unix 守护进程的库。

回答by S.Lott

First, read up on mail aliases. A mail alias will do this inside the mail system without you having to fool around with daemons or services or anything of the sort.

首先,阅读邮件别名。邮件别名将在邮件系统内部执行此操作,而无需您使用守护程序或服务或任何类型的东西。

You can write a simple script that will be executed by sendmail each time a mail message is sent to a specific mailbox.

您可以编写一个简单的脚本,每次将邮件消息发送到特定邮箱时,sendmail 都会执行该脚本。

See http://www.feep.net/sendmail/tutorial/intro/aliases.html

请参阅http://www.feep.net/sendmail/tutorial/intro/aliases.html

If you really want to write a needlessly complex server, you can do this.

如果你真的想编写一个不必要的复杂服务器,你可以这样做。

nohup python myscript.py &

That's all it takes. Your script simply loops and sleeps.

这就是全部。您的脚本只是循环并休眠。

import time
def do_the_work():
    # one round of polling -- checking email, whatever.
while True:
    time.sleep( 600 ) # 10 min.
    try:
        do_the_work()
    except:
        pass

回答by jhwist

You can use fork() to detach your script from the tty and have it continue to run, like so:

您可以使用 fork() 将脚本与 tty 分离并让它继续运行,如下所示:

import os, sys
fpid = os.fork()
if fpid!=0:
  # Running as daemon now. PID is fpid
  sys.exit(0)

Of course you also need to implement an endless loop, like

当然你还需要实现一个无限循环,比如

while 1:
  do_your_check()
  sleep(5)

Hope this get's you started.

希望这让你开始。

回答by the_drow

Here's a nice class that is taken from here:

这是从这里获取的一个不错的类:

#!/usr/bin/env python

import sys, os, time, atexit
from signal import SIGTERM

class Daemon:
        """
        A generic daemon class.

        Usage: subclass the Daemon class and override the run() method
        """
        def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
                self.stdin = stdin
                self.stdout = stdout
                self.stderr = stderr
                self.pidfile = pidfile

        def daemonize(self):
                """
                do the UNIX double-fork magic, see Stevens' "Advanced
                Programming in the UNIX Environment" for details (ISBN 0201563177)
                http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
                """
                try:
                        pid = os.fork()
                        if pid > 0:
                                # exit first parent
                                sys.exit(0)
                except OSError, e:
                        sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
                        sys.exit(1)

                # decouple from parent environment
                os.chdir("/")
                os.setsid()
                os.umask(0)

                # do second fork
                try:
                        pid = os.fork()
                        if pid > 0:
                                # exit from second parent
                                sys.exit(0)
                except OSError, e:
                        sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
                        sys.exit(1)

                # redirect standard file descriptors
                sys.stdout.flush()
                sys.stderr.flush()
                si = file(self.stdin, 'r')
                so = file(self.stdout, 'a+')
                se = file(self.stderr, 'a+', 0)
                os.dup2(si.fileno(), sys.stdin.fileno())
                os.dup2(so.fileno(), sys.stdout.fileno())
                os.dup2(se.fileno(), sys.stderr.fileno())

                # write pidfile
                atexit.register(self.delpid)
                pid = str(os.getpid())
                file(self.pidfile,'w+').write("%s\n" % pid)

        def delpid(self):
                os.remove(self.pidfile)

        def start(self):
                """
                Start the daemon
                """
                # Check for a pidfile to see if the daemon already runs
                try:
                        pf = file(self.pidfile,'r')
                        pid = int(pf.read().strip())
                        pf.close()
                except IOError:
                        pid = None

                if pid:
                        message = "pidfile %s already exist. Daemon already running?\n"
                        sys.stderr.write(message % self.pidfile)
                        sys.exit(1)

                # Start the daemon
                self.daemonize()
                self.run()

        def stop(self):
                """
                Stop the daemon
                """
                # Get the pid from the pidfile
                try:
                        pf = file(self.pidfile,'r')
                        pid = int(pf.read().strip())
                        pf.close()
                except IOError:
                        pid = None

                if not pid:
                        message = "pidfile %s does not exist. Daemon not running?\n"
                        sys.stderr.write(message % self.pidfile)
                        return # not an error in a restart

                # Try killing the daemon process       
                try:
                        while 1:
                                os.kill(pid, SIGTERM)
                                time.sleep(0.1)
                except OSError, err:
                        err = str(err)
                        if err.find("No such process") > 0:
                                if os.path.exists(self.pidfile):
                                        os.remove(self.pidfile)
                        else:
                                print str(err)
                                sys.exit(1)

        def restart(self):
                """
                Restart the daemon
                """
                self.stop()
                self.start()

        def run(self):
                """
                You should override this method when you subclass Daemon. It will be called after the process has been
                daemonized by start() or restart().
                """

回答by faisal00813

how about using $nohupcommand on linux?

$nohup在linux上使用命令怎么样?

I use it for running my commands on my Bluehost server.

我用它在我的 Bluehost 服务器上运行我的命令。

Please advice if I am wrong.

如果我错了,请指教。

回答by Kishore K

You can also make the python script run as a service using a shell script. First create a shell script to run the python script like this (scriptname arbitary name)

您还可以使用 shell 脚本使 python 脚本作为服务运行。首先创建一个shell脚本来运行这样的python脚本(scriptname任意名称)

#!/bin/sh
script='/home/.. full path to script'
/usr/bin/python $script &

now make a file in /etc/init.d/scriptname

现在在 /etc/init.d/scriptname 中创建一个文件

#! /bin/sh

PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/home/.. path to shell script scriptname created to run python script
PIDFILE=/var/run/scriptname.pid

test -x $DAEMON || exit 0

. /lib/lsb/init-functions

case "" in
  start)
     log_daemon_msg "Starting feedparser"
     start_daemon -p $PIDFILE $DAEMON
     log_end_msg $?
   ;;
  stop)
     log_daemon_msg "Stopping feedparser"
     killproc -p $PIDFILE $DAEMON
     PID=`ps x |grep feed | head -1 | awk '{print }'`
     kill -9 $PID       
     log_end_msg $?
   ;;
  force-reload|restart)
     
import sys
import os
from signal import SIGTERM
from abc import ABCMeta, abstractmethod



class Daemon(object):
    __metaclass__ = ABCMeta


    def __init__(self, pidfile):
        self._pidfile = pidfile


    @abstractmethod
    def run(self):
        pass


    def _daemonize(self):
        # decouple threads
        pid = os.fork()

        # stop first thread
        if pid > 0:
            sys.exit(0)

        # write pid into a pidfile
        with open(self._pidfile, 'w') as f:
            print >> f, os.getpid()


    def start(self):
        # if daemon is started throw an error
        if os.path.exists(self._pidfile):
            raise Exception("Daemon is already started")

        # create and switch to daemon thread
        self._daemonize()

        # run the body of the daemon
        self.run()


    def stop(self):
        # check the pidfile existing
        if os.path.exists(self._pidfile):
            # read pid from the file
            with open(self._pidfile, 'r') as f:
                pid = int(f.read().strip())

            # remove the pidfile
            os.remove(self._pidfile)

            # kill daemon
            os.kill(pid, SIGTERM)

        else:
            raise Exception("Daemon is not started")


    def restart(self):
        self.stop()
        self.start()
stop ##代码## start ;; status) status_of_proc -p $PIDFILE $DAEMON atd && exit 0 || exit $? ;; *) echo "Usage: /etc/init.d/atd {start|stop|restart|force-reload|status}" exit 1 ;; esac exit 0

Now you can start and stop your python script using the command /etc/init.d/scriptname start or stop.

现在您可以使用命令 /etc/init.d/scriptname start 或 stop 启动和停止您的 python 脚本。

回答by Chris Johnson

cronis clearly a great choice for many purposes. However it doesn't create a service or daemon as you requested in the OP. cronjust runs jobs periodically (meaning the job starts and stops), and no more often than once / minute. There are issues with cron-- for example, if a prior instance of your script is still running the next time the cronschedule comes around and launches a new instance, is that OK? crondoesn't handle dependencies; it just tries to start a job when the schedule says to.

cron显然是许多用途的绝佳选择。但是,它不会按照您在 OP 中的要求创建服务或守护程序。 cron只是定期运行作业(意味着作业开始和停止),并且不超过一次/分钟。有问题cron- 例如,如果您的脚本的先前实例在下次cron计划出现并启动新实例时仍在运行,是否可以? cron不处理依赖项;它只是在计划说到时尝试开始工作。

If you find a situation where you truly need a daemon (a process that never stops running), take a look at supervisord. It provides a simple way to wrapper a normal, non-daemonized script or program and make it operate like a daemon. This is a much better way than creating a native Python daemon.

如果您发现确实需要守护进程(永不停止运行的进程)的情况,请查看supervisord. 它提供了一种简单的方法来包装一个普通的、非守护进程的脚本或程序,并使其像守护进程一样运行。这比创建原生 Python 守护进程要好得多。

回答by Richard

Use whatever service manager your system offers - for example under Ubuntu use upstart. This will handle all the details for you such as start on boot, restart on crash, etc.

使用您的系统提供的任何服务管理器 - 例如在 Ubuntu 下使用upstart。这将为您处理所有细节,例如启动时启动、崩溃时重新启动等。

回答by Fomalhaut

I would recommend this solution. You need to inherit and override method run.

我会推荐这个解决方案。您需要继承和覆盖方法run

##代码##