Python:Linux下守护进程编写

来源:互联网 发布:淘宝客经典文案 编辑:程序博客网 时间:2024/05/18 22:14

1. 调用fork()以便父进程可以退出,这样就将控制权归还给运行你程序的命令行或shell程序。需要这一步以便保证新进程不是一个进程组头领进程(process group leader)。下一步,‘setsid()’,会因为你是进程组头领进程而失败。进程调用fork函数时,操作系统会新建一个子进程,它本质上与父进程完全相同。子进程从父进程继承了多个值的拷贝,比如全局变量和环境变量。两个进程唯一的区别就是fork的返回值。child(子)进程接收返回值为0,而父进程接收子进程的pid作为返回值。调用fork函数后,两个进程并发执行同一个程序,首先执行的是调用了fork之后的下一行代码。父进程和子进程既并发执行,又相互独立;也就是说,它们是异步执行的。

2. 调用‘setsid()’ 以便成为一个进程组和会话组的头领进程。由于一个控制终端与一个会话相关联,而且这个新会话还没有获得一个控制终端,我们的进程没有控制终端,这对于守护程序来说是一件好事。 

3. 再次调用‘fork()’所以父进程(会话组头领进程)可以退出。这意味着我们,一个非会话组头领进程永远不能重新获得控制终端。

4. 调用‘chdir("/")’确认我们的进程不保持任何目录于使用状态。不做这个会导致系统管理员不能卸装(umount)一个文件系统,因为它是我们的当前工作目录。 [类似的,我们可以改变当前目录至对于守护程序运行重要的文件所在目录

5. 调用‘umask(0)’以便我们拥有对于我们写的任何东西的完全控制。我们不知道我们继承了什么样的umask。 [这一步是可选的](译者注:这里指步骤5,因为守护程序不一定需要写文件)

6. 调用‘close()’关闭文件描述符0,12。这样我们释放了从父进程继承的标准输入,标准输出,和标准错误输出。我们没办法知道这些文描述符符可能已经被重定向去哪里。注意到许多守护程序使用‘sysconf()’来确认‘_SC_OPEN_MAX’的限制。‘_SC_OPEN_MAX’告诉你每个进程能够打开的最多文件数。然后使用一个循环,守护程序可以关闭所有可能的文件描述符。你必须决定你需要做这个或不做。如果你认为有可能有打开的文件描述符,你需要关闭它们,因为系统有一个同时打开文件数的限制。 

7. 为标准输入,标准输出和标准错误输出建立新的文件描述符。即使你不打算使用它们,打开着它们不失为一个好主意。准确操作这些描述符是基于各自爱好;比如说,如果你有一个日志文件,你可能希望把它作为标准输出和标准错误输出打开,而把‘/dev/null’作为标准输入打开;作为替代方法,你可以将‘/dev/console’作为标准错误输出和/或标准输出打开,而‘/dev/null’作为标准输入,或者任何其它对你的守护程序有意义的结合方法。(译者注:一般使用dup2函数原子化关闭和复制文件描述符。

# Core modulesimportatexitimportosimportsysimporttimeimportsignalclassDaemon(object):    """A generic daemon class.Usage: subclass the Daemon class and override the run() method"""    def __init__(self, pidfile, stdin=os.devnull,                 stdout=os.devnull, stderr=os.devnull,                 home_dir='.', umask=022, verbose=1):        self.stdin = stdin        self.stdout = stdout        self.stderr = stderr        self.pidfile = pidfile        self.home_dir = home_dir        self.verbose = verbose        self.umask = umask        self.daemon_alive = True    def daemonize(self):        """Do the UNIX double-fork magic, see Stevens' "AdvancedProgramming 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(self.home_dir)        os.setsid()        os.umask(self.umask)        # 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)        if sys.platform != 'darwin': # This block breaks on OS X            # Redirect standard file descriptors            sys.stdout.flush()            sys.stderr.flush()            si = file(self.stdin, 'r')            so = file(self.stdout, 'a+')            if self.stderr:                se = file(self.stderr, 'a+', 0)            else:                se = so            os.dup2(si.fileno(), sys.stdin.fileno())            os.dup2(so.fileno(), sys.stdout.fileno())            os.dup2(se.fileno(), sys.stderr.fileno())        def sigtermhandler(signum, frame):            self.daemon_alive = False            signal.signal(signal.SIGTERM, sigtermhandler)            signal.signal(signal.SIGINT, sigtermhandler)        if self.verbose >= 1:            print "Started"        # Write pidfile        atexit.register(            self.delpid) # Make sure pid file is removed if we quit        pid = str(os.getpid())        file(self.pidfile, 'w+').write("%s\n" % pid)    def delpid(self):        os.remove(self.pidfile)    def start(self, *args, **kwargs):        """Start the daemon"""        if self.verbose >= 1:            print "Starting..."        # 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        except SystemExit:            pid = None        if pid:            message = "pidfile %s already exists. Is it already running?\n"            sys.stderr.write(message % self.pidfile)            sys.exit(1)        # Start the daemon        self.daemonize()        self.run(*args, **kwargs)    def stop(self):        """Stop the daemon"""        if self.verbose >= 1:            print "Stopping..."        # Get the pid from the pidfile        pid = self.get_pid()        if not pid:            message = "pidfile %s does not exist. Not running?\n"            sys.stderr.write(message % self.pidfile)            # Just to be sure. A ValueError might occur if the PID file is            # empty but does actually exist            if os.path.exists(self.pidfile):                os.remove(self.pidfile)            return # Not an error in a restart        # Try killing the daemon process        try:            i = 0            while 1:                os.kill(pid, signal.SIGTERM)                time.sleep(0.1)                i = i + 1                if i % 10 == 0:                    os.kill(pid, signal.SIGHUP)        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)        if self.verbose >= 1:            print "Stopped"    def restart(self):        """Restart the daemon"""        self.stop()        self.start()    def get_pid(self):        try:            pf = file(self.pidfile, 'r')            pid = int(pf.read().strip())            pf.close()        except IOError:            pid = None        except SystemExit:            pid = None        return pid    def is_running(self):        pid = self.get_pid()        print(pid)        return pid and os.path.exists('/proc/%d' % pid)    def run(self):        """You should override this method when you subclass Daemon.It will be called after the process has beendaemonized by start() or restart()."""


0 0
原创粉丝点击