2

我在远程机器上运行一个守护进程:mydaemon。此守护程序应始终持续运行。

当我在远程机器上运行作业时,它还会启动一个轻量级 python 服务器进程my_remote_server.py

我可以发送到my_remote_server.py的命令之一是重新启动mydaemon,我正在尝试这样做:

os.system("killall mydaemon")
subprocess.Popen(["mydaemon"], stdin=None, stdout=None, stderr=None, close_fds=True)

当我的工作结束时,my_remote_server.py应该终止,但mydaemon应该继续运行。但是我看到my_remote_server.py作为僵尸进程卡住了(这导致系统看不到我的工作已终止)

  820 root            Z   [my_remote_serve]
  834 root        552 S   /usr/sbin/telnetd -l /bin/sh 
  835 root        836 S   /bin/sh 
  844 root        672 S   mydaemon 

我想从孩子(mydaemon )中分离父( my_remote_server.py),但我不知道如何。

-- 我的python版本是2.5.4

编辑:

我想我现在更好地理解了守护进程,但我仍然无法让守护进程分离

为简洁起见,我在这里省略了错误处理

os.system("killall mydaemon")

if(os.fork() > 0):
    return True # my_remote_server.py returns to handle additional commands

os.setsid()
if(os.fork() > 0):
    exit(0) # first child exits after becoming session leader

os.execlp("mydaemon") # have the second child run as the daemon

这是我调用 restart_mydaemon 函数之前的 ps 列表

252 root        672 S   mydaemon
286 root       4552 S   /usr/bin/python my_remote_server.py

这是在restart_mydaemon 之后,第一个孩子被僵尸化(不应该消失吗?)

286 root       4552 S   /usr/bin/python my_remote_server.py
300 root            Z   [my_remote_serve]
304 root        672 S   [mydaemon]

这是作业终止的时间(my_remote_server.py 应该已经退出,但它是一个僵尸,但是,此时第一个孩子已经退出)

286 root            Z   [my_remote_serve]
304 root       1012 S   [mydaemon]
4

3 回答 3

1

将一个进程变成一个守护进程是一个多步骤的过程。您的部分问题是您的父进程没有正确等待子进程终止(阅读有关等待/waitpid),

  • 您的进程必须关闭所有打开的文件描述符(stdin、stdout、stderr)
  • 您的流程需要(更改目录,设置 umask,无论您想要/需要什么)
  • 你必须分叉一个进程
  • 该流程必须成为流程组负责人
  • 您必须再次分叉,以完全脱离(祖)父母

    try:
        #free parent, detach from process group
        pid = os.fork()
        if( pid>0 ):
            exit(0) #parent exits
    except OSError, e:
        raise Exception("%s [%d]" % (e.strerror, e.errno))
    #become session leader, process group leader, detach from controlling terminal
    os.setsid()
    try:
        #prevent zombie process, make init cleanup
        pid = os.fork()
        if( pid>0 ):
            exit(0) #parent exits
    except OSError, e:
        raise Exception("%s [%d]" % (e.strerror, e.errno))
    #change directories, close stdin/out/err, etc
    os.chdir(MYDIR)
    os.umask(MYMASK)
    #close files (including stdin, stdout, stderr)
    #(re)open new stdin/stdout, if desired
    
于 2013-10-14T22:14:16.917 回答
0

在这里查看有关守护进程如何工作的参考/很好的解释:

http://code.activestate.com/recipes/278731/

总的来说,这非常重要,我会尝试使用 python-daemon 库或类似库:

https://pypi.python.org/pypi/python-daemon/

于 2013-10-14T21:44:16.030 回答
0

我不会调用子流程来执行此操作。子进程 imo 应该只用于运行您无法导入的命令。

因此,在这种情况下,您的 my_remote_server.py 可以导入 mydaemon 并运行它。

你有两种方法可以做到这一点。您可以将 my_remote_server 设为父级,将 mydaemon 设为子级。或者有一个新脚本作为父脚本并产生 2 个子脚本(mydaemon、my_remote_server)。

例如。下面我创建一个子进程。全局范围,因为我需要在 tornado 的 HTTP 处理程序中访问它。我设置了 Web 服务器。启动子进程,然后启动 Web 服务器。当端点被命中时,处理程序运行并且子进程从全局范围中被拉出。我们终止子进程并打印。您可以向龙卷风添加更多端点以再次启动子节点或停止然后重新启动它。

远程.py:

import multiprocessing
import deamon
import tornado.ioloop
import tornado.web

child_pro = multiprocessing.Process(target=deamon.run_deamon)
child_pro.daemon = True


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        global child_pro
        child_pro = child_pro  # type: multiprocessing.Process
        child_pro.terminate()
        print ("Child killed")


def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])


if __name__ == "__main__":
    global child_pro
    app = make_app()
    app.listen(8888)
    child_pro.start()
    tornado.ioloop.IOLoop.current().start()

守护进程.py:

import time


def run_deamon():
    while True:
        time.sleep(2)
        print ('child alive')
于 2019-01-31T11:58:00.050 回答