2011-04-30 78 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解決了這個問題。