首页 > 解决方案 > 如何在给定 pid 的情况下通知守护进程

问题描述

所以我一直在用 python 弄湿我的脚,试图构建一个与 gnome 通知 ui 相关的提醒系统。基本的想法是你在你的 shell 中输入一个命令remind me to check on dinner in 20 min,然后在 20 分钟内你会收到一个桌面通知,上面写着“检查晚餐”。我这样做的方法是让脚本解析消息并写入应该发送通知的时间以及应该发送到日志文件的消息。

通知由 python 守护程序触发。我正在使用我在网上找到的这个守护进程设计。我看到的问题是,当这个守护进程运行时,它占用了我 100% 的 CPU!我剥离了守护进程正在执行的所有代码,当所有守护进程正在执行时我仍然遇到这个问题

while True:
 last_modified = os.path.getmtime(self.logfile)

我认为这是一种不好的方法,我应该在有新提醒时通知守护进程,然后大部分时间提醒守护进程应该处于睡眠状态。现在这只是一个想法,但是当我只知道守护进程 pid 时,我很难找到有关“如何通知进程”的资源。因此,如果我暂停了守护进程,我time.sleep(time_to_next_notification)是否有办法向守护进程发送信号,让它知道有新的提醒?

标签: pythonpython-2.7daemon

解决方案


尽管我相信您最好使用侦听端口的服务器 - 客户端类型解决方案,但您要问的是 100% 可能使用signalandos库。这种方法不适用于多线程程序,但是因为信号仅由 python 中的父线程处理。此外,windows 不会以相同的方式实现信号,因此选项更加有限。

信号

“客户端”进程可以使用os.kill(pid, signal). 您将必须检查可用信号并确定要使用哪个信号(signal.NSIG可能是一个不错的选择,因为它不应该妨碍任何其他默认行为)。

启动时的“守护程序”进程必须注册一个处理程序,以便在接收到您选择的信号时要做什么。处理程序是您必须定义的函数,它接收接收到的信号本身以及 execuiton ( def handler(signum, frame):) 的当前堆栈帧。如果你只用这个处理程序做一件事,并且它不需要知道它被调用时发生了什么,你可以忽略这两个参数。signal.signal然后您必须使用ex:注册处理程序signal.signal(signal.NSIG, handler)

从那里你会想找到一些合适的方法来等到下一个信号而不消耗太多资源。这可以像循环os.sleep 命令一样简单,或者您可以尝试变得花哨。我不确定从信号处理程序返回时如何 100% 恢复执行,因此您可能需要关注递归深度(即,确保您不会在每次处理信号时都进行递归,否则您只会能够在需要重新启动之前处理有限数量的信号)。

服务器

让进程在端口(通常称为服务器,但在功能上与您的“守护程序”描述相同)而不是监听操作系统信号有几个主要优点。

  • 端口能够在信号只能触发事件的情况下发送数据
  • 端口更类似于跨平台
  • 端口与多线程一起玩得很好[r]
  • 端口使通过网络发送消息变得容易(即:从手机创建提醒并在 PC 上执行)

一次等待多件事

为了解决一次等待多个进程的需要(侦听输入以及等待传递下一个通知),您有很多选择:

  • 信号实际上可能是一个很好的用例,因为signal.SIGALRM它可以用作方便地重新设置的闹钟(如果您使用的是 UNIX)。您将以与以前相同的方式设置处理程序,并简单地为下一个通知设置警报。设置警报后,您可以简单地继续在端口上侦听新任务。如果有新任务进入,再次设置警报将覆盖现有任务,因此处理程序需要检索下一个排队的通知并在完成第一个任务后重新设置警报。

  • 线程既可以用于轮询通知任务队列,也可以创建一个单独的线程来等待每个任务。这不是一个特别优雅的解决方案,但它会有效且易于实施。

  • 最优雅的解决方案可能是使用asyncio协程,但是我不太精通 asyncio,并且承认它们比线程更令人困惑。


推荐阅读