When I run this Python script with os.system on Ubuntu 12.04:
import os, signal
signal.signal(signal.SIGABRT, lambda *args: os.write(2, 'HANDLER\n'))
print 'status=%r' % os.system('sleep 5')
, and then I send SIGABRT to the script process many times within 5 seconds, I get the following output:
status=0
HANDLER
This indicates that the signal delivery was blocked until sleep 5 exited, and then only a single signal was delivered.
However, with 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)
, all individual signals are delivered early:
HANDLER
HANDLER
HANDLER
cstatus=0
To distinguish the magic in glibc from the magic in Python, I rewrote the Python script in C, so os.system became 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);
}
Signals got delivered early:
HANDLER
HANDLER
HANDLER
system=0x0
So I inferred that the magic is in Python 2.7, not in eglibc. But where is the magic? Based on the strace output and looking at the posix_system function in Modules/posixmodule.c, I couldn't figure out how Python blocks the signal until os.system returns.
Relevant code from 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);
}
Maybe the magic is in Py_BEGIN_ALLOW_THREADS?
Do I understand correctly that it's impossible for my Python signal handler (set up by signal.signal) to execute until os.system returns?
Is it because signal handlers are blocked (on the Python level, not on the OS level) until Py_END_ALLOW_THREADS returns?
Here is the strace output of the Python code with os.system: http://pastebin.com/Wjn9KBye