Reputation: 1407
I can create a subprocess in Python and suspend it like this:
proc = subprocess.pOpen(['binary', 'arg1', 'arg2'])
# Subprocess was created in a running state
proc_handle = psutil.Process(pid=proc.pid)
proc_handle.suspend()
# Subprocess is now suspended
proc_handle.resume()
# Subprocess is now running
Question: How can I start the subprocess in a suspended state? I understand that very little time will elapse between creating the subprocess and suspending it, but I am interested in a seamless solution. Also, I know I could easily wrap the subprocess in a parent process which will wait for a resume signal before proceeding, but I would like a less hacky, more elegant solution if one exists. Furthermore, I know I could create a class wrapper around the subprocess, that won't create it until I actually want to run it, but then I won't have a PID assigned to it yet and I won't be able to check stats like memory footprint of the unstarted process.
I would love it if I could do this:
cmd = ['binary', 'arg1', 'arg2']
proc = subprocess.pOpen(cmd, autostart=False) # autostart argument does not exist
# Subprocess was created, so it had a PID and has a memory footprint,
# but is suspended and no code had been run
I recognized while I was writing the above example of pOpen with autostart=False that this may not be possible because of how processes are run. If I want to break at the start of the main() function, then some code will have already been executed as part of static initialization. Ideally, the subprocess will be suspended before that step, just after the process memory is created and initialized.
Note: I am particularly interested in solving this issue in a linux environment but a platform-independent solution would be great if it exists.
Upvotes: 3
Views: 1733
Reputation: 295291
fork()
, Before The exec()
This one's easy, though not so useful:
import subprocess, os, signal
proc = subprocess.Popen(
['binary', 'arg1', 'arg2'],
preexec_fn=lambda: os.kill(os.getpid(), signal.SIGSTOP),
)
Why "not so useful"? Because while preexec_fn
runs in the new PID, your intent appears to involve gathering statistics on memory usage (to the extent that that can be done before a program does its malloc()
calls), and until the exec
has taken place, the program whose memory you'll be measuring is the Python interpreter.
exec()
, Before Invocation Of main()
First, compile an audit module for the Linux loader (ld.so
), akin to the following:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
unsigned int la_version(unsigned int version) {
return version;
}
/* To have stronger guarantees of immediacy, I might replace this with flock()ing a
* lockfile, and releasing that lock from the parent when ready to continue. YMMV,
* consider this a stub intended to be replaced.
*/
void la_preinit(uintptr_t *cookie) {
kill(getpid(), SIGSTOP);
unsetenv("LD_AUDIT"); /* avoid our children getting stopped the same way */
}
Then, specify it when running your subprocess:
newEnv = dict(os.environ)
newEnv['LD_AUDIT'] = '/path/to/above/library.so'
proc = subprocess.Popen(['binary', 'arg1', 'arg2'], env=newEnv)
la_preinit
is invoked by the loader just before main()
is started.
Upvotes: 3