Search code examples
python-2.7subprocesssignalssegmentation-faultsigchld

Why does a child process killed due to a segmentation fault not die immediately?


I need to write a C++ code which accepts a certain input and prints the respective output. This code is meant to be run using the Python subprocess module. Regardless of inputs and outputs, I need to make sure that the Python code does not terminate prematurely due to runtime errors the C++ code encounters. The basic nature of the C++ code is as follows

int main()
{
  /*The pointer is initialized to NULL to simulate a segmentation fault
    Also the n is meant for simulating input.*/
  int *p=NULL,n;   
  cin>>n;
  cout<<*p<<endl;  //This causes a segmentation fault.
}

The Python code which runs it, is as follows:

from subprocess import *
from signal import *

def handler(signum,frame):
    raise RuntimeError("Runtime Error")

call(["g++","try.cpp"])
a = Popen(["stdbuf","-i0","-o0","-e0","./a.out"],stdin = PIPE,stdout = PIPE)
try:
    #Handler for signal due to termination of child process
    signal(SIGCHLD,handler)    
    a.stdin.write("1\n")
    temp = a.stdout.readline()
except RuntimeError as e:
    print e
    print a.returncode   

#Returncode of process killed due to SIGSEGV is -11
    if a.returncode == -11:   
        print "Segmentation Fault Occurred"

This is the issue. Even though the C++ code experiences a segmentation fault, the signal handler is invoked, the RuntimeError is raised, but the returncode of the Popen object is none, indicating that the process still lives.
Now if the following change is made to the except block:

a.wait()
print a.returncode        
if a.returncode == -11:   
    print "Segmentation Fault Occurred"

The issue is resolved. The output shows that the returncode of the Popen object is -11 and "Segmentation Fault Occurred" is printed to the screen.
The exact same happens if I try to simulate a floating point exception due to divide-by-zero.
Why does this happen?


Solution

  • From the documentation

    Popen.wait() Wait for child process to terminate. Set and return returncode attribute.

    So returncode is not set until wait is called.

    Alternately you could perform a non-blocking check to see if process is terminated using poll, which would set returncode as well if terminated.

    Popen.poll() Check if child process has terminated. Set and return returncode attribute.

    note that you don't really need the signal call (not sure if it's portable on Windows). The code could be simplified like this:

    a = Popen(["stdbuf","-i0","-o0","-e0","./a.out"],stdin = PIPE,stdout = PIPE)
    a.stdin.write("1\n")
    a.stdin.flush()
    temp = a.stdout.readline()
    if temp:
        # do something
        print("output "+temp)
    else:
        # empty string: recieved process has ended, probably unexpectedly
        # because it should have printed something
        pass
    
    returncode = a.wait()
    
    #Returncode of process killed due to SIGSEGV is -11
    if returncode  == -11:   
        print("Segmentation Fault Occurred")
    

    note that you have to a.stdin.flush() to be sure the input reaches the c++ program.