0

I'm curious, why the code below freezes. When I kill python3 interpreter, "cat" process remains as a zombie. I expect the subprocess will be terminated before main process finished.

When I send manually SIGTERM to cat /dev/zero, the process is correctly finished (almost immediately)

#!/usr/bin/env python3
import subprocess
import re
import os
import sys
import time
from PyQt4 import QtCore

class Command(QtCore.QThread):
#    stateChanged = QtCore.pyqtSignal([bool])

    def __init__(self):
        QtCore.QThread.__init__(self)
        self.__runned = False
        self.__cmd = None
        print("initialize")

    def run(self):
        self.__runned = True

        self.__cmd = subprocess.Popen(["cat /dev/zero"], shell=True, stdout=subprocess.PIPE)
        try:
            while self.__runned:
                print("reading via pipe")
                buf = self.__cmd.stdout.readline()
                print("Buffer:{}".format(buf))
        except:
            logging.warning("Can't read from subprocess (cat /dev/zero) via pipe")
        finally:
            print("terminating")
            self.__cmd.terminate()
            self.__cmd.kill()

    def stop(self):
        print("Command::stop stopping")
        self.__runned = False
        if self.__cmd:
            self.__cmd.terminate()
            self.__cmd.kill()
            print("Command::stop stopped")


def exitApp():
    command.stop()
    time.sleep(1)
    sys.exit(0)


if __name__ == "__main__":
    app = QtCore.QCoreApplication(sys.argv)
    command = Command()
#    command.daemon = True
    command.start()
    timer = QtCore.QTimer()
    QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), exitApp)
    timer.start(2 * 1000)
    sys.exit(app.exec_())
nickhar
  • 19,981
  • 12
  • 60
  • 73
Miso
  • 161
  • 2
  • 11
  • I have a vague feeling you need to call `p.wait()` after killing it to clean up the zombie process, but I can't remember why. – Thomas K Jun 08 '12 at 12:24
  • Great, it works! I'll test it on the affected machine at my work on Monday. Just adding self.quit() at the end of run() function and self.__cmd.wait() at the end of stop() function helped to correctly finish and don't leave a zombie. Thank you Thomas – Miso Jun 08 '12 at 19:13
  • You're welcome - glad to hear my hunch had some basis in reality. – Thomas K Jun 08 '12 at 21:24

2 Answers2

2

As you noted yourself, the reason for zombie is that signal is caught by shell and doesn't effect process created by it. However there is a way to kill shell and all processes created by it; you have to use process group feature. See How to terminate a python subprocess launched with shell=True Having said that if you can manage without shell=True that's always preferable - see my answer here.

Community
  • 1
  • 1
Piotr Dobrogost
  • 41,292
  • 40
  • 236
  • 366
0

I solved this problem in a different way, so here's the result: I have to call subprocess.Popen with shell=False, because otherwise it creates 2 processes (shell and the process) and __cmd.kill() send signal to shell while "process" remains as a zombie

Miso
  • 161
  • 2
  • 11