11

I want to start several subprocesses with a programm, i.e. a module foo.py starts several instances of bar.py.

Since I sometimes have to terminate the process manually, I need the process id to perform a kill command.

Even though the whole setup is pretty “dirty”, is there a good pythonic way to obtain a process’ pid, if the process is started via os.system?

foo.py:

import os
import time
os.system("python bar.py \"{0}\ &".format(str(argument)))
time.sleep(3)
pid = ???
os.system("kill -9 {0}".format(pid))

bar.py:

import time
print("bla")
time.sleep(10) % within this time, the process should be killed
print("blubb")
Sebastian Werk
  • 1,568
  • 2
  • 17
  • 30
  • *"therefore the subprocess `Popen(["python", "bar.py", "arguments"])` is too slow."* -- have you actually measured it? [The question that you've linked](http://stackoverflow.com/questions/10888846/python-subprocess-module-much-slower-than-commands-deprecated) shows different case and anyway the time difference is a couple of milliseconds. – jfs Dec 04 '13 at 16:56
  • Yes, I have measured it. It was around 20 to 30ms, which was in my specific case too slow, since I had a scrolling graphic, which had to be updated multiple times per second. `os.spawnl` gave me the desired functionality, even though it added around one second to start each process, but without stopping my main process. It is still not the ideal solution, but for my case bettern than `subprocess.Popen`. – Sebastian Werk Dec 04 '13 at 19:18
  • You should've mentioned it in the question (ideally, with a complete minimal code example that other people can try). Beware `spawn` inherits all inheritable file descriptors (`Popen` closes them (Python 3)). Can you start processes before hand and pause them and then only unpause them (send input or a signal)? Why can't you use `bar.py` script in the same process? btw, why `spawn` would add a whole second to the starting of a process compared to `Popen`? – jfs Dec 04 '13 at 22:03
  • Some details about my program: A continously (several times per second) updated diagram scrolls from right to left side. In `bar.py`, several time-consuming calculations are made. While the calculations are not done, it is written “calculating” in the diagram. If the user makes any input, the calculations have to be terminated and new calculations have to be started. It is less important, that the results of calculations are made quickly, than that the diagram does move smoothly. The reason for the extra second remains unclear to me. – Sebastian Werk Dec 05 '13 at 09:01
  • After having made several other changes, I realized, that I cannot reproduce either the longer time for starting a process via Popen nor the extra second when starting a process via Popen. I will alter the question to make the accepted answer fit and revoke my solution. Sorry for bothering you all – I have no idea, where my previous results came from. – Sebastian Werk Dec 05 '13 at 09:15

3 Answers3

15

os.system return exit code. It does not provide pid of the child process.

Use subprocess module.

import subprocess
import time
argument = '...'
proc = subprocess.Popen(['python', 'bar.py', argument], shell=True)
time.sleep(3) # <-- There's no time.wait, but time.sleep.
pid = proc.pid # <--- access `pid` attribute to get the pid of the child process.

To terminate the process, you can use terminate method or kill. (No need to use external kill program)

proc.terminate()
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • 1
    @SebastianWerk, According to [`os.spawn*` documentation](http://docs.python.org/3/library/os.html#os.spawnl), "Note that the subprocess module provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using these functions. ..." – falsetru Nov 26 '13 at 13:49
  • I know, therefor I used it in first place, but it takes around 20ms to start one process on my computer, and this is a bit too long ): – Sebastian Werk Nov 26 '13 at 13:58
  • @SebastianWerk, How about posting **EDIT** part of your question as answer and accepting it instead of trying to close your question? – falsetru Nov 26 '13 at 14:12
  • does this work if you want the main process (the code above) to go down after spawning the new one and writing a pid file? – Tommy May 10 '16 at 14:29
4

Sharing my solution in case it can help others:

I took the info from this page to run a fortran exe in the background. I tried to use os.forkpty to get the pid of it, but it didnt give the pid of my process. I cant use subprocess, because I didnt find out how it would let me run my process on the background.

With help of a colleague I found this:

exec_cmd = 'nohup ./FPEXE & echo $! > /tmp/pid'

os.system(exec_cmd)

In case of wanting to append pids to the same file, use double arrow.

Damaris
  • 41
  • 3
  • how to capture the stdout to a file if we use this approach? `nohup ./FPEXE | tee my_log.log & echo $! > /tmp/pid` . Becuase the pid capture is the proccess id of `tee` and not the pid of the actual command. – sai harsha vardhan Feb 23 '22 at 16:37
3

You could use os.forkpty() instead, which, as result code, gives you the pid and fd for the pseudo terminal. More documentation here: http://docs.python.org/2/library/os.html#os.forkpty

Tobias Wärre
  • 793
  • 5
  • 11