0

I'm trying to launch from Python a server to score PMML models in Java. Once the calculations have been made, I want to be able to close that server (i.e. terminate the process). I'm on Windows 10. However, I'm finding it way more confusing than I thought it would be.

First I launch the server like this:

p = subprocess.Popen('START /B java -jar openscoring-server-executable-1.4.3.jar',
                      shell=True)

I make it run on the background so that the CMD window doesn't show up when the user runs the script. To make this possible I think that the shell=True parameter is necessary.

When I try to kill the process using this line:

subprocess.call("TASKKILL /F /PID {pid} /T".format(pid=p.pid))

The process is still running. In fact, the PID that p.pid returns doesn't even exist in the Task Manager. I've confirmed this when trying to use the solution provided in this answer:

def kill(proc_pid):
    process = psutil.Process(proc_pid)
    for proc in process.children(recursive=True):
        proc.kill()
    process.kill()

kill(p.pid)

This returns the exception NoSuchProcess: no process found with pid 5280.

I can see via the Task Manager the PID assigned to the process I want to kill:

enter image description here

But I don't know how to find that PID within Python to terminate it. The answers I've found in the site don't seem to work with processes running in the background.

EDIT:

I think this could be solved if I was able to run the command with shell=False, but I don't know how to do it because of the START /B. This returns an error:

p = subprocess.Popen(['START', '/B', 'java', '-jar openscoring-server-executable-1.4.3.jar'])
Tendero
  • 1,136
  • 2
  • 19
  • 34
  • Did you try without `START /B`? – CristiFati Oct 03 '18 at 16:01
  • @CristiFati Yes, it opens a CMD window and I want to avoid that. That's why I introduced it in the command. I believe this has to do with the fact that the PID I get is the one corresponding to the shell created and not the process launched from it. I think this could be solved by using `shell=False` but I don't know if it is possible when using `START /B`. – Tendero Oct 03 '18 at 16:04
  • Hmm, I created a dummy *Java* program, with a endless loop that that prints something and sleeps for one second, ran it normally (with `subprocess.Popen`), but it didn't open a new window, instead its output was redirected in the console where I started the *Python* process (its parent). the behavior was the same when adding `START /B` (and it was detached from its parent too). – CristiFati Oct 03 '18 at 16:25
  • You are getting the PID of the `START` command. Maybe try `p = subprocess.Popen(['javaw', '-jar openscoring-server-executable-1.4.3.jar'], creationflags=(subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS), close_fds=True)` Starting with `javaw` makes java not spawn a console. The rest lets the process run independently (in theory -- I got this from googling). – Steven Rumbalski Oct 03 '18 at 16:30
  • Just run JVM directly, why do you use `start` in the first place? Furthermore, once you launch your Java app directly as a subprocess you can then use [`Popen.kill()`](https://docs.python.org/3/library/subprocess.html#subprocess.Popen.kill) to directly kill it instead of offseting everything to `taskkill.exe`. – zwer Oct 03 '18 at 18:05
  • 1
    @StevenRumbalski, `CREATE_NEW_PROCESS_GROUP` is generally pointless for a GUI process such as javaw.exe. The only WinAPI that uses process groups is `GenerateConsoleCtrlEvent`, so the program would have to call `AllocConsole` or `AttachConsole` in order for the process group to be relevant. `DETACHED_PROCESS` is also pointless for a GUI app. Its purpose is to detach the child from the current console without allocating a new one. – Eryk Sun Oct 04 '18 at 00:38

3 Answers3

1

This line does not work as you need to provide ALL arguments and exact strings.

if process.cmdline() == args:

I changed it to include some of the strings and partial strings also:

def process_get(*args):
        """Returns the process matching ``args``.
        Relies on ``args`` matching the argument list of the process to find.

        Args:
            *args: process arguments, e.g. ["java", "jar", "partialjarname"]

        Returns:
            :obj:`process`
        """
        import psutil
        for process in psutil.process_iter():
            try:
                if all(x in " ".join(process.cmdline()) for x in args):
                    return process
            except psutil.AccessDenied:
                pass
        return None
Gil Cohen
  • 836
  • 7
  • 12
0

If you can't figure out the right PID, I might have an alternative solution leveraging the arguments you used to start the process. It's not as nice, since you will iterate over the process tree until you find a hit, but this way it will find the process for sure.

Just remember that this might not always return the correct process if you have multiple processes with the same argument running.

def process_get(*args):
    """Returns the process matching ``args``.
    Relies on ``args`` matching the argument list of the process to find.

    Args:
        *args: process arguments, e.g. ["java", "-jar", "somejar.jar"]

    Returns:
        :obj:`process`
    """
    import psutil
    for process in psutil.process_iter():
        try:
            if process.cmdline() == args:
                return process
        except psutil.AccessDenied:
            pass
    return None
Tim Lehr
  • 13
  • 4
0

You could just search for your app here and close.

import psutil
import os

ls = []
for p in psutil.process_iter():
    try:
        name = p.name()
        cmdline = p.cmdline()
        exe = p.exe()
        pid = p.pid
    except (psutil.AccessDenied, psutil.ZombieProcess):
        continue
    except psutil.NoSuchProcess:
        continue
eatmeimadanish
  • 3,809
  • 1
  • 14
  • 20