How does ^C work in Python?

Dan Drown dan-ntp at drown.org
Tue Sep 27 16:20:32 UTC 2016


Quoting Hal Murray <hmurray at megapathdsl.net>:
> Thanks.  I assume "system call" includes library calls as well as kernel
> calls.
>
> So DNS lookups are non-interruptable, just like in c.

Yes.  From the relevant python documentation:

Although Python signal handlers are called asynchronously as far as  
the Python user is concerned, they can only occur between the “atomic”  
instructions of the Python interpreter. This means that signals  
arriving during long calculations implemented purely in C (such as  
regular expression matches on large bodies of text) may be delayed for  
an arbitrary amount of time.

[TL;DR - signal handlers need the interpreter to be in a sane state,  
so they have to wait]

Some layer is re-issuing the poll call in a socket.getaddrinfo call  
instead of exiting back to the python interpreter to handle the signal.

poll([{fd=3, events=POLLIN}], 1, 5000)  = ? ERESTART_RESTARTBLOCK  
(Interrupted by signal)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Interrupted system call)
poll([{fd=3, events=POLLIN}], 1, 3359)  = ? ERESTART_RESTARTBLOCK  
(Interrupted by signal)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Interrupted system call)
poll([{fd=3, events=POLLIN}], 1, 2192)  = ? ERESTART_RESTARTBLOCK  
(Interrupted by signal)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Interrupted system call)
poll([{fd=3, events=POLLIN}], 1, 1960)  = 0 (Timeout)

I guess the good news is it eventually times out.

Perl's workaround for this problem by having the default sigint  
handler be the simple C exit which doesn't need the interpreter state  
to be sane.  If you use a perl sigint handler, it has the same  
problem.  Does python have an option to just exit with the sigint  
signal handler and not rely on the interpreter state?

== test /etc/resolv.conf ==
# needs an IP that doesn't respond at all
nameserver 192.0.2.1

== test python dns lookup ==
#!/usr/bin/env python
import socket
print(socket.getaddrinfo("example.com", "ntp", socket.AF_UNSPEC,  
socket.SOCK_DGRAM, socket.IPPROTO_UDP, 0))

== test perl dns lookup ==
#!/usr/bin/perl
use Socket qw(getaddrinfo);
use Data::Dumper;
print Dumper(getaddrinfo("example.com", "ntp"));



More information about the devel mailing list