2011-04-30 56 views
9

这是我的其他职位Installing signal handler with Python的后续行动。简而言之,除非Init为特定信号安装了信号处理程序,否则Linux将阻止所有信号到PID 1(包括SIGKILL);以防止有人将终止信号发送到PID1时发生内核恐慌。我一直有的问题是,Python中的signal模块似乎没有以系统识别的方式安装信号处理程序。我的Python Init脚本似乎完全忽略了所有信号,因为我认为它们被阻止。Linux阻止信号到Python初始化

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

这是解决此问题的最佳方法吗?在没有信号模块的情况下安装信号处理器有没有更好的方法? (我一点也不关心)

这是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) 
+0

这实际上属于codereview.SE,但是对于在Python中实现'init'的酷想法+1。 – 2011-04-30 20:25:17

+0

我认为这可能是我应该发布的地方,但我没有设置这种做法,并认为可能有很多想法我没有想过。 – tMC 2011-04-30 20:30:45

回答

6

我确实有点调试的下KVM和我发现内核递送信号为PID 1当信号处理程序是由标准信号模块安装。但是,当接收到信号时,“某事”会导致进程的克隆产生,而不是打印预期的输出。

这里是strace的输出当我发送HUP到非工作init.sig-MOD:

strace output

这将导致一个新的进程中运行(PID 23),这是初始化的克隆的.sig-MOD:

clone of init as pid 23

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

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

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

无论是ctypes的调用的signal()系统调用和暴发户的sigaction() 系统调用设置SA_RESTART标志时,处理程序注册。设置 这个标志表示当某个系统调用(读,写,等待, nanosleep等)执行或阻塞进程 时收到一个信号,信号处理程序完成后,系统调用应该自动重新启动 。应用程序不会意识到这一点。

当Python的信号模块注册处理程序时,它通过调用siginterrupt(signum, 1)来使SA_RESTART 标志为零。这对系统说:“当一个信号中断 系统调用时,在信号处理程序完成 将errno设置为EINTR并从系统调用返回后”。这让开发者可以通过 来处理这个问题并决定是否重新启动系统调用。

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

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

我急于尝试这个 - 即使它不能解决这个问题,这是很好的参考!一旦我有机会测试它,我会重新评论。 – tMC 2011-05-01 15:44:15

+0

这并没有工作 - 感谢这个想法,虽然非常有洞察力 – tMC 2011-05-01 17:18:28

+0

好,但要澄清:你的基于ctypes的init代码正在工作,对吧? – samplebias 2011-05-01 17:45:47

1

的问题是兼容性问题与Python有一个旧的Linux线程编译针对uClibc的0.9.31。用0.9.32-rc3编译并使用NPTL解决了这个问题。