【问题标题】:python daemon killed (by kernel?)python 守护进程被杀死(被内核?)
【发布时间】:2023-09-14 10:10:01
【问题描述】:

我有一个使用来自http://www.jejik.com/files/examples/daemon.py 的模块的python2.7 守护进程

这个进程很重,大约有 40 GB RAM 使用量和 9 个子线程。服务器使用 RHEL 6.3,具有 192 GB RAM 和足够的 CPU 能力。

启动该进程后,它会持续大约 3-7 个小时,但随后它被某人杀死,可能是内核。但是我在 dmesg 和内核日志(我手动激活)中找不到任何提示,那里什么都没有。当不作为守护进程启动时,我只是在终端中收到消息:“killed”。

已做好以下预防措施:

  • 在 /proc//oom_score_adj 中重置 oom 分数,以便 oom 杀手在资源排序时不会选择进程
  • 将所有 rlimits(可以增加)增加到最大值
  • 设置进程 nice/priority 更高 (prio -15)

这个问题在采取这些预防措施之前就已经存在,所以他们不负责杀戮

我还有一种机制可以捕获所有异常、STDERR、STDOUT 并将所有内容记录到旋转的日志文件中。但是在进程结束之前并没有什么有趣的事情。

进程中使用的模块包括:oracle_cx、ibm_db、suds、wsgi_utils。但是他们总是在发生错误时写入日志。

有人知道如何追溯杀戮吗?谁和为什么?

提前谢谢你

【问题讨论】:

  • 您是否启用了 coredump 以查看您是否在 kill 时获得转储?
  • 不,我没有。问题是,我们使用大约 128 GB 的 SSD,当进程有 40 GB RAM 时,没有足够的空间用于核心转储。
  • 鉴于您的大 RAM 并假设您的进程不会暂时填满整个内容(这会导致内核杀死它),您可以将 coredump 存储在 RAM 上(通过 tmpfs)。跨度>

标签: python linux daemon rhel


【解决方案1】:

我想我找到了根本原因,它可能是 Python2.7 中的一个错误 在捕获所有可捕获的信号并忽略它们之后,我可以跟踪更多错误消息并获得 socket.error 的提示。问题是,这样的错误将首先触发 SIGTERM(trus 尝试终止进程),然后写入 STDERR。我捕获所有 STDOUT 和 STDERR 的机制可以记录消息,因为主进程已被杀死。无论如何,这在守护进程中是一个问题。这些是进程终止前日志中的最后几行

2013-05-07 11:05:53,194 - STDERR - ERROR - Traceback (most recent call last):
2013-05-07 11:05:53,304 - STDERR - ERROR -   File "/var/lib/netcam_epd/lib/python2.7/SocketServer.py", line 582, in process_request_thread
2013-05-07 11:05:53,415 - STDERR - ERROR -     self.finish_request(request, client_address)
2013-05-07 11:05:53,489 - STDERR - ERROR -   File "/var/lib/netcam_epd/lib/python2.7/SocketServer.py", line 323, in finish_request
2013-05-07 11:05:53,587 - STDERR - ERROR -     self.RequestHandlerClass(request, client_address, self)
2013-05-07 11:05:53,684 - STDERR - ERROR -   File "/var/lib/netcam_epd/lib/python2.7/SocketServer.py", line 640, in __init__
2013-05-07 11:05:53,835 - STDERR - ERROR -     self.finish()
2013-05-07 11:05:53,887 - STDERR - ERROR -   File "/var/lib/netcam_epd/lib/python2.7/SocketServer.py", line 693, in finish
2013-05-07 11:05:54,084 - STDERR - ERROR -     self.wfile.flush()
2013-05-07 11:05:54,182 - STDERR - ERROR -   File "/var/lib/netcam_epd/lib/python2.7/socket.py", line 303, in flush
2013-05-07 11:05:54,326 - STDERR - ERROR -     self._sock.sendall(view[write_offset:write_offset+buffer_size])
2013-05-07 11:05:54,387 - STDERR - ERROR - error: [Errno 32] Broken pipe

显然,这是由于试图写入不可写的套接字造成的。我认为库应该通过适当的返回值更好地处理这个问题,而不仅仅是抛出错误/异常,因为套接字可以在正常运行中随时关闭。

我会验证这是否真的是根本原因。

【讨论】:

    【解决方案2】:

    要查看进程被终止时谁登录,请使用命令last

    如果当时没有人登录,则该进程被某种信号杀死。

    由于这是 Python,找出进程终止的最简单方法是为所有信号编写一个信号处理程序并记录它们。见here how to write a signal handler。见this question how to catch them all

    如果您获得核心转储,请附加一个具有足够空间的外部硬盘。或者使用ulimit将核心大小限制为1GB;这可能足以看到它在哪里崩溃了。

    或者,使用诸如gdb 之类的调试器启动进程;当“核心转储”信号发送到进程时,它将确保您得到提示。

    【讨论】:

    • 没有人杀死这个过程,我很确定。不能修改 SIGKILL 处理程序。
    • 如果您在终端中看到“killed”,这并不意味着它被SIGKILL 杀死。为任何未处理的信号打印该消息。而且“相当肯定”并没有削减:-)
    • 守护进程已经处理了 SIGTERM、SIGABRT、SIGINT 和 SIGSTOP。其他信号会产生其他消息然后“杀死”。我现在正在尝试按照 Mansour 的建议使用 ramdisk 获取核心转储。
    • 我看到一个没有处理 SIGCHLD 的进程被“杀死”。还要寻找试图保护系统的过度热心的监控进程。
    • 你能说出一些这样的监控进程吗?我确实想过,但我不知道。顺便说一句,我也使用 monit 尝试在进程死亡时重新启动进程,但是当进程死亡时它停止监视,但这是另一个问题。