【问题标题】:Linux blocking signals to Python initLinux阻塞信号到Python init
【发布时间】:2011-08-16 04:57:44
【问题描述】:

这是对我的另一篇帖子Installing signal handler with Python 的跟进。简而言之,除非 Init 为特定信号安装了信号处理程序,否则 Linux 会阻止所有到 PID 1 的信号(包括 SIGKILL);如果有人要向 PID1 发送终止信号,以防止内核恐慌。我遇到的问题是,Python 中的 signal 模块似乎没有以系统识别的方式安装信号处理程序。我的 Python Init 脚本似乎完全忽略了所有信号,因为我认为它们被阻止了。

我似乎找到了解决办法;使用ctypes 在libc(在本例中为uClibc)中安装带有signal() 函数的信号处理程序。下面是一个基于 python 的测试初始化​​。它在 TTY2 上打开一个 shell,我可以从中向 PID1 发送信号进行测试。它似乎在用于测试的 KVM 中工作(我愿意与任何感兴趣的人共享 VM)

这是解决此问题的最佳方法吗?有没有“更好”的方法来安装没有信号模块的信号处理程序? (我根本不关心便携)

这是 Python 中的错误吗?

#!/usr/bin/python

import os
import sys
import time

from ctypes import *

def SigHUP():
    print "Caught SIGHUP"
    return 0

def SigCHLD():
    print "Caught SIGCHLD"
    return 0

SIGFUNC = CFUNCTYPE(c_int)
SigHUPFunc = SIGFUNC(SigHUP)
SigCHLDFunc = SIGFUNC(SigCHLD)

libc = cdll.LoadLibrary('libc.so.0')
libc.signal(1, SigHUPFunc) # 1 = SIGHUP
libc.signal(17, SigCHLDFunc) # 17 = SIGCHLD

print "Mounting Proc: %s" % libc.mount(None, "/proc", "proc", 0, None)

print "forking for ash"
cpid = os.fork()
if cpid == 0:
    os.closerange(0, 4)
    sys.stdin = open('/dev/tty2', 'r')
    sys.stdout = open('/dev/tty2', 'w')
    sys.stderr = open('/dev/tty2', 'w')
    os.execv('/bin/ash', ('ash',))

print "ash started on tty2"

print "sleeping"
while True:
    time.sleep(0.01)

【问题讨论】:

  • 这确实属于 codereview.SE,但 +1 是为了在 Python 中实现 init 的酷想法。
  • 我认为这可能是我应该发布的地方,但我没有设置这种方法,并且认为可能有很多我没有想到的想法。

标签: python c linux signals init


【解决方案1】:

我在 KVM 下做了一些调试,发现当标准信号模块安装信号处理程序时,内核正在向 pid 1 传递信号。但是,当接收到信号时,“某事”会导致生成进程的克隆,而不是打印预期的输出。

这是我将 HUP 发送到不工作的 init.sig-mod 时的 strace 输出:

这会导致一个新进程运行(pid 23),它是 init.sig-mod 的克隆:

我没有时间深入研究原因,但这进一步缩小了范围。可能与 Python 的信号传递逻辑有关(它注册了一个 C 挂钩,在调用时会调用您的字节码函数)。 ctypes 技术绕过了这一点。相关的 Python 源文件是 Python/pythonrun.cModules/signalmodule.c,以防您想仔细查看。

旧信息 -- 我不确定这会解决您的问题,但可能会让您更接近。一世 比较了安装信号处理程序的这些不同方式:

  • 通过 Python 的信号模块安装处理程序。
  • Upstart 的信号处理程序。
  • 使用 ctypes 直接调用 signal() 系统调用。
  • C 中的一些快速测试。

ctypes-invoked signal() 系统调用和 Upstart 的 sigaction() 系统调用在注册处理程序时设置SA_RESTART 标志。环境 此标志表示当进程正在接收到信号时 在某些系统调用(读、写、等待、 nanosleep 等),在信号处理程序完成后,系统调用应该是 自动重启。应用程序不会意识到这一点。

当 Python 的信号模块注册一个处理程序时,它会将 SA_RESTART 归零 通过调用siginterrupt(signum, 1) 进行标记。这对系统说“当一个 在信号处理程序完成后,系统调用被信号中断 将 errno 设置为 EINTR 并从系统调用返回”。这留给开发人员 处理这个并决定是否重新启动系统调用。

您可以通过这种方式注册您的信号来设置 SA_RESTART 标志:

import signal
signal.signal(signal.SIGHUP, handler)
signal.siginterrupt(signal.SIGHUP, False)

【讨论】:

  • 我迫不及待地想试试这个——即使它不能解决这个问题,这是一个很好的参考!一旦我有机会测试它,我会重新评论。
  • 那行不通-感谢您的想法,非常有见地
  • 好的,但要澄清一下:您的基于 ctypes 的初始化代码正在运行,对吧?
  • @simplebias 我在项目博客 (www.teeos.org) 上链接了您的帖子。如果你愿意,我可以删除它。
  • @tMC 没问题 :-) 对我来说仍然很奇怪的是 strace 显示signals 模块注册版本和 ctypes 注册版本之间的唯一区别是 ctypes 设置 SA_RESTART 标志。您有在 VM 中运行 TeeOS 并进行调试的快速方法吗?
【解决方案2】:

该问题是针对 uClibc 0.9.31 使用旧 linux 线程编译的 Python 的兼容性问题。针对 0.9.32-rc3 进行编译并使用 NPTL 已解决此问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 2011-05-26
    • 2015-12-14
    • 1970-01-01
    • 2015-01-20
    • 2016-05-24
    相关资源
    最近更新 更多