Linux的阻塞信号到Python的init [英] Linux blocking signals to Python init

查看:183
本文介绍了Linux的阻塞信号到Python的init的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个跟进我的其他职务与安装信号处理程序蟒蛇。总之,Linux的街区所有信号PID 1(包括SIGKILL),除非初始化安装了特定信号的信号处理函数;以prevent内核恐慌,如果有人要终止信号发送给PID1。我一直有这个问题,是它似乎在Python中信号模块没有在系统识别的方式安装信号处理程序。我的Python的init脚本是看似完全忽略所有的信号,因为我认为他们被封锁。

我似乎已经找到了解决办法;使用 ctypes的安装与信号()功能libc中的信号处理(在这种情况下,uClibc的)。下面是一个基于Python的测试初始化​​。它打开上TTY2一个shell,我可以发送信号PID1进行测试。这似乎在使用测试KVM IM的工作(我愿意与任何人有兴趣分享VM)

这是解决这个问题的最好方法?是否有一个'好'的方式来安装信号处理程序没有信号模块? (我不是在所有关心可移植性)

这是Python中的错误吗?

 #!的/ usr / bin中/蟒蛇进口OS
进口SYS
进口时间从ctypes的导入*高清SIGHUP():
    打印中招SIGHUP
    返回0高清SIGCHLD():
    打印中招SIGCHLD
    返回0SIGFUNC = 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打印安装PROC:%的%libc.mount(无的/ proc,处理,0,无)打印分叉灰
CPID = os.fork()
如果CPID == 0:
    os.closerange(0,4)
    sys.stdin =开放('的/ dev / TTY2','R')
    为sys.stderr =开放('的/ dev / TTY2','W')
    sys.stderr来=打开('的/ dev / TTY2','W')
    os.execv('/斌/灰',('灰',))打印灰开始TTY2打印沉睡
而真正的:
    time.sleep(0.01)


解决方案

我做了一些调试下,KVM,我发现了内核的安装传递信号为PID 1时,信号处理程序由标准信号的模块。然而,在接收​​到信号时东西使处理的一个克隆进行衍生,而不是打印期望的输出

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

这导致运行的新的进程(pid 23),这是init.sig-MOD克隆

我没有时间深入挖掘的原因,但这种缩小进一步的事情。也许是与Python的信号传递逻辑(它会注册一个C勾调用时它调用你的字节code函数)。该ctypes的技术绕过这一点。有关Python源文件的Python / pythonrun.c 模块/ signalmodule.c ,如果​​你想仔细看看。

旧信息 - 我不知道这将解决你的问题,但可能让你更接近。一世
相比这些不同方式的信号处理器安装:


  • 通过Python的信号模块安装的处理程序。

  • 新贵的信号处理程序。

  • 使用ctypes的调用信号()系统调用直接。

  • 一些快速测试的温度。

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

在Python的信号模块注册的处理程序,它的归零SA_RESTART
通过调用 siginterrupt标志(Signum的,1)。这是说给系统时,
系统调用是由一个信号中断,信号处理程序完成后,
将errno设置为EINTR和系统调用返回。这使得它的开发者
处理这个问题,并决定是否重新启动系统调用。

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

 进口信号
signal.signal(signal.SIGHUP,处理程序)
signal.siginterrupt(signal.SIGHUP,假)

This is a follow up to my other post Installing signal handler with Python. In short, Linux blocks all signals to PID 1 (including SIGKILL) unless Init has installed a signal handler for a particular signal; as to prevent kernel panic if someone were to send a termination signal to PID1. The issue I've been having, is it would seem that the signal module in Python doesn't install signal handlers in a way the system recognises. My Python Init script was seemingly, completely ignoring all signals as I think they were being blocked.

I seem to have found a solution; using ctypes to install the signal handlers with the signal() function in libc (in this case uClibc). Below is a python based test init. It opens a shell on TTY2 from which I can send signals to PID1 for testing. It seems to work in the KVM im using for testing (I'm willing to share the VM with anyone interested)

Is this the best way around this issue? Is there a 'better' way to install the signal handlers without the signal module? (I am not at all concerned with portably)

Is this a bug in 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)

解决方案

I did a bit of debugging under KVM and I found that the kernel is delivering signals to pid 1 when the signal handlers are installed by the standard signal module. However, when the signal is received "something" causes a clone of the process to be spawned, rather than printing the expected output.

Here is the strace output when I send HUP to the non-working init.sig-mod:

Which results in a new process running (pid 23) which is a clone of init.sig-mod:

I didn't have time to dig deeper into the cause, but this narrows things further. Probably something to do with Python's signal delivery logic (it registers a C hook which invokes your bytecode function when called). The ctypes technique bypasses this. The relevant Python source files are Python/pythonrun.c and Modules/signalmodule.c, in case you want to take a closer look.

Old Info -- I'm not sure this will solve your problem, but might get you closer. I compared these different ways signal handlers are installed:

  • Installing a handler via Python's signal module.
  • Upstart's signal handlers.
  • Using ctypes to call the signal() syscall directly.
  • Some quick tests in C.

Both the ctypes-invoked signal() system call and Upstart's sigaction() syscalls set the SA_RESTART flag when the handler is registered. Setting this flag indicates that when a signal is received while the process is executing or blocking inside certain syscalls (read, write, wait, nanosleep, etc), after the signal handler completes the syscall should be automatically restarted. The application won't be aware of this.

When Python's signal module registers a handler, it zeros the SA_RESTART flag by calling siginterrupt(signum, 1). This says to the system "when a system call is interrupted by a signal, after the signal handler completes set errno to EINTR and return from the syscall". This leaves it up to the developer to handle this and decide whether to restart the system call.

You can set the SA_RESTART flag by registering your signal this way:

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

这篇关于Linux的阻塞信号到Python的init的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆