2014-11-22 18 views
5

Kiedy uruchomić ten skrypt Pythona z os.system na Ubuntu 12.04:W jaki sposób Python blokuje sygnały podczas os.system ("sleep ...")?

import os, signal 
signal.signal(signal.SIGABRT, lambda *args: os.write(2, 'HANDLER\n')) 
print 'status=%r' % os.system('sleep 5') 

, a potem wysłać SIGABRT do procesu skryptu wiele razy w ciągu 5 sekund, pojawia się następujący komunikat:

status=0 
HANDLER 

Oznacza to, że dostarczenie sygnału zostało zablokowane do czasu zakończenia sleep 5, a następnie dostarczono tylko jeden sygnał.

Jednak z subprocess.call:

import os, signal, subprocess 
signal.signal(signal.SIGABRT, lambda *args: os.write(2, 'HANDLER\n')) 
print 'cstatus=%r' % subprocess.call('sleep 5', shell=True) 

, wszystkie poszczególne sygnały są dostarczane wcześnie:

HANDLER 
HANDLER 
HANDLER 
cstatus=0 

Aby odróżnić magię w glibc od magii w Pythonie, przepisałem skrypt Pythona w C , więc os.system stał się system (3):

#include <errno.h> 
#include <signal.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
static void handler(int signum) { (void)signum; write(2, "HANDLER\n", 8); } 
int main(int argc, char **argv) { 
    int got; 
    struct sigaction sa; 
    (void)argc; (void)argv; 
    memset(&sa, 0, sizeof sa); 
    sa.sa_handler = handler; 
    if (0 != sigaction(SIGABRT, &sa, NULL)) return 3; 
    got = system("sleep 5"); 
    return !printf("system=0x%x\n", got); 
} 

Sygnały został dostarczony na początku:

HANDLER 
HANDLER 
HANDLER 
system=0x0 

więc wywnioskować, że magia jest w Pythonie 2.7, nie w eglibc. Ale gdzie jest magia? Opierając się na wyjściu strace i patrząc na funkcję posix_system w Modules/posixmodule.c, nie mogłem dowiedzieć się, jak Python blokuje sygnał do momentu powrotu do os.system.

odpowiedni kod z Modules/posixmodule.c:

static PyObject *posix_system(PyObject *self, PyObject *args) { 
    char *command; 
    long sts; 
    if (!PyArg_ParseTuple(args, "s:system", &command)) return NULL; 
    Py_BEGIN_ALLOW_THREADS 
    sts = system(command); 
    Py_END_ALLOW_THREADS 
    return PyInt_FromLong(sts); 
} 

Może magia jest w Py_BEGIN_ALLOW_THREADS?

Czy poprawnie rozumiem, że moja obsługa sygnału w języku Python (skonfigurowana przez signal.signal) jest niemożliwa do wykonania, dopóki nie zostanie zwrócony os.system?

Czy to dlatego, że procedury obsługi sygnału są blokowane (na poziomie Pythona, a nie na poziomie systemu operacyjnego), dopóki nie powróci Py_END_ALLOW_THREADS?

Oto wyjście strace kodu Pythona z os.system: http://pastebin.com/Wjn9KBye

Odpowiedz

4

Może magia jest w PY_BEGIN_ALLOW_THREADS?

Magia jest głównie w system się. system nie może zwrócić EINTR, więc implementacja libc przechodzi do bitu, aby wznowić jego wait proces potomny. Oznacza to, że przy korzystaniu z os.system, kontrola nigdy nie wraca do pythona, dopóki nie zakończy się podstawowa system, a zatem mechanizmy obsługi sygnału Pythona nie są wywoływane w odpowiednim czasie.

subprocess.call jednak zasadniczo to robi:

# Compare subprocess.py:Popen/_eintr_retry_call(os.waitpid, self.pid, 0) 
while True: 
    try: 
    return os.waitpid(the_child_pid, 0) 
    except OSError, e: 
    if e.errno == errno.EINTR: # signal.signal() handler already invoked 
     continue 
    raise 

Tutaj kontrolować robi powrót do pytona gdy instrumentem bazowym wait zostaje przerwany. OSError/EINTR monituje pythona, aby sprawdzić, czy zostały wyzwolone jakiekolwiek sygnały, a jeśli tak, wywołać blok kodu dostarczony przez użytkownika związany z tym sygnałem. (I w ten sposób interpreter dostosowuje semantykę sygnału systemu: ustaw flagę i sprawdź ją między "atomowymi" operacjami pythonowymi, wywołując kod użytkownika, jeśli to konieczne.)

Powiązane problemy