问题
I'm developing a Minecraft (Bukkit) server manager. It's porpuse is only to start it, stop it, back it up and check if it is running. The last one causes the problem. If I do like this to get the pid of the server:
subprocess.Popen(["screen", "-dmS", "minecraft-server", "serverstart.sh"])
I get the pid of the screen command, not the startup-script. However it seems like the pid is always one underneath the pid of the startup-script, but I suppose this isn't relaiable. How can I get the pid of the java process?
EDIT: I tried this, but ps returns with exit code 1 and no child pid. I think this is because screen closes down imidiately.
check_output(['ps', '--ppid', str(Popen(['screen', '-dmS', 'test']).pid), '--no-headers', '-o', 'pid'])
回答1:
If you have the process ID of the screen (the parent process, which you can access with p.pid
assuming you used p = Subprocess.Popen(...)
), you can get the child process ids through something like
ps --ppid <SCREEN_PID> --no-headers -o pid
There's also psutil.Process(<SCREEN_PID>).get_children()
available from the psutil module which might be preferred to parsing the output of ps since (I think) it parses /proc
directly.
There are also some functions inside Python's standard os module that allow you to do some stuff with process IDs directly, but nothing that will get the child process IDs of a parent process id or a process group id.
The following code:
#!/bin/env python
import subprocess, random, string, re
import psutil
SERVER_SCRIPT = "./serverstart.sh"
def get_random_key(strlen):
return 'K'+''.join(random.choice(string.hexdigits) for x in range(strlen-1))
def find_screen_pid(name):
ph = subprocess.Popen(["screen", "-ls"], stdout=subprocess.PIPE)
(stdout,stderr) = ph.communicate()
matches = re.search(r'(\d+).%s' % name, stdout, re.MULTILINE)
if(matches):
pids = matches.groups()
if(len(pids) == 1): return int(pids[0])
else: raise Exception("Multiple matching PIDs found: %s" % pids)
raise Exception("No matching PIDs found")
def get_child_pids(parent_pid):
pp = psutil.Process(parent_pid)
return [ int(cp.pid) for cp in pp.get_children()]
# Generate a random screen name, in case you're running multiple server instances
screenname = "minecraft-server-" + get_random_key(5)
print("Creating screen session named: %s" % screenname)
subprocess.Popen(["screen", "-dmS", screenname, SERVER_SCRIPT]).wait()
spid = find_screen_pid(screenname) # Display some output
print("Screen PID: %d" % spid)
cpids = get_child_pids(spid)
print("Child PIDs: %s" % cpids)
Produces the output:
./screen-pid.py Creating screen session named: minecraft-server-K77d1 Screen PID: 2274 Child PIDs: [2276]
You can access the child pid from the children pid list with cpids[0]
.
The script simply spawns the screen process with a specific name, then finds the parent process id and from that, the children process ids.
The random characters appended to the screen name are there in case you're running multiple instances using the same script. If you're not, you can remove all that, but it makes no difference leaving it.
The method of finding the parent process id (parsing the output of screen -ls
) probably isn't the best, you might alternatively iterate through the processes using psutils.process_iter()
. But this seems to work.
回答2:
Assuming that you want the PID of the process running in screen, I answered that in another question on this site. Here is the contents of that answer:
You can get the PID of the screen sessions here like so:
$ screen -ls
There are screens on:
1934.foo_Server (01/25/15 15:26:01) (Detached)
1876.foo_Webserver (01/25/15 15:25:37) (Detached)
1814.foo_Monitor (01/25/15 15:25:13) (Detached)
3 Sockets in /var/run/screen/S-ubuntu.
Let us suppose that you want the PID of the program running in Bash in the foo_Monitor
screen session. Use the PID of the foo_Monitor
screen session to get the PID of the bash
session running in it by searching PPIDs (Parent PID) for the known PID:
$ ps -el | grep 1814 | grep bash
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 1815 1814 0 80 0 - 5520 wait pts/1 00:00:00 bash
Now get just the PID of the bash
session:
$ ps -el | grep 1814 | grep bash | awk '{print $4}'
1815
Now we want the process with that PID. Just nest the commands, and this time use the -v
flag on grep bash
to get the process that is not bash:
echo $(ps -el | grep $(ps -el | grep 1814 | grep bash | awk '{print $4}') | grep -v bash | awk '{print $4}')
23869
Just replace 1814 with the real PID or your screen session:
echo $(ps -el | grep $(ps -el | grep SCREEN_SESSION_PID | grep bash | awk '{print $4}') | grep -v bash | awk '{print $4}')
回答3:
You have to wrap your java command into a shell script that obtains and returns the pid. See http://ss64.org/viewtopic.php?id=1495 for an example.
来源:https://stackoverflow.com/questions/15840738/get-pid-of-command-ran-by-screen