【问题标题】:Clean shutdown of a Python script彻底关闭 Python 脚本
【发布时间】:2012-07-18 15:10:29
【问题描述】:

我有一个用 Python 编写的线程服务器,我开始使用以下 shell 脚本:

#!/bin/bash

base_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

public_dns=$(curl -s http://169.254.169.254/latest/meta-data/public-hostname)
echo $public_dns > "$base_path/client/address"

cd "$base_path/server"
python "server.py" &
echo $! > "$base_path/server_pid"
echo "Server running"

我将 PID 回显到一个文件,以便我可以使用另一个 shell 脚本关闭服务器:

#!/bin/bash

base_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

kill -9 `cat "$base_path/server_pid"`
rm "$base_path/server_pid"
rm "$base_path/client/address"

但是,我知道,考虑到服务器有许多线程将 I/O 输入到网络和硬盘中,这是一种不好的方法......所以我想做的是让第二个脚本以某种方式与服务器交互,并且告诉它启动一个关闭序列,该序列将干净地关闭所有线程、关闭和归档日志等。

现在我知道了atexit,并以这种方式对其进行了测试:

import atexit
def byebye(o):
    o.write('stop')
    o.flush()
    o.close()

o = open('log','w')
o.write('start')
o.flush()

atexit.register(byebye, o)

while True:
    pass

但是当我kill -9 进程时,byebye() 不会被解雇。我应该使用almighty kill -9 以外的命令吗?我将如何关闭该进程?

【问题讨论】:

  • 那么套接字呢?您可以写入本地文件套接字或使用网络套接字(特定端口可能且仅通过 localhost 进行通信)。在那里你可以告诉它关闭。杀好像有点狠……
  • 只要您没有任何try/except KeyboardInterrupt 子句,您就可以使用kill -2,因为它会发送SIGINT。一个更干净的解决方案是在 python 中注册一个调用sys.exit 的信号处理程序——虽然我不确定它是如何工作的,因为你在一个线程环境中......
  • 我曾想过将“停止”一词写入一个将由服务器持续监控的文件,但那是另一个要启动的线程,我认为也许有更优雅的方式来做这在 Python 中
  • cd 是不必要的; base_path=$(dirname "${BASH_SOURCE[0]}")
  • @mgilson:经过一些测试,我认为您的方法是最好的,请将其发布为答案,以便我接受。

标签: python bash process exit kill


【解决方案1】:

在我看来,您正在尝试实现一个守护进程。 关于 python 中的守护进程实现有各种参考:

上一个 stackoverlow 问题: How do you create a daemon in Python?

关于守护进程的 PEP: http://www.python.org/dev/peps/pep-3143/

python 模块实现 PEP3143 : http://pypi.python.org/pypi/python-daemon/

请注意,PEP 是草稿。

关于守护进程的更多信息:http://en.wikipedia.org/wiki/Daemon_%28computing%29

通常,守护进程的启动、停止和重新启动如下:

mydaemon start
mydaemon stop
mydaemon restart

所以你不需要知道 PID 或其他任何东西来阻止它。

【讨论】:

  • 服务器基于已经作为守护进程运行的 SocketServer (docs.python.org/library/socketserver.html)(由server_thread.daemon = True),所以将我的脚本包装为启动守护进程的守护进程似乎有点不必要?
  • 我不是守护进程和网络应用程序方面的专家,但似乎这个 server_thread.daemon 选项提供的功能与 python-daemon 模块提供的功能不同。特别是,python-daemon 提供的方便的命令行界面没有包含在 SocketServer 模块中。
【解决方案2】:

我不是特别习惯于使用线程进行编程,但您可以发送SIGINT 或其他用户定义的信号,而不是发送kill -9(对应于SIGKILL)。 SIGINT(我的系统上的kill -2)很好,因为python已经理解了那个。 (当 python 捕捉到该信号时,它会引发 KeyboardInterrupt),但任何信号都可以工作。您只需要注册一个信号处理程序即可干净地退出您的程序。

【讨论】:

    【解决方案3】:

    kill 9 是原子驱动的——你不需要自己清理。更好的方法是使用不同的、更温和的信号(例如,HUP 通常用于向服务器进程发出信号,表明该关闭了),并教你的 python 代码如何优雅地处理它。

    signal module documentation 应该可以帮助您入门。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-03
      • 2013-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多