1

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'])
4

3 回答 3

4

如果您有屏幕的进程 ID(父进程,p.pid假设您使用过,您可以访问它p = Subprocess.Popen(...)),您可以通过类似的方式获取子进程 ID

ps --ppid <SCREEN_PID> --no-headers -o pid

psutil模块也psutil.Process(<SCREEN_PID>).get_children()提供了它,它可能比解析 ps 的输出更可取,因为(我认为)它直接解析。/proc

Python 的标准 os 模块中还有一些函数允许您直接使用进程 ID 执行一些操作,但不会获取父进程 ID 或进程组 ID 的子进程 ID。


以下代码:

#!/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)

产生输出:

./screen-pid.py
创建名为:minecraft-server-K77d1 的屏幕会话
屏幕PID:2274
儿童 PID:[2276]

您可以使用 访问子 pid 列表中的子 pid cpids[0]

该脚本简单地生成具有特定名称的屏幕进程,然后找到父进程 ID 并从中找到子进程 ID。

如果您使用相同的脚本运行多个实例,则附加到屏幕名称的随机字符会出现。如果你不是,你可以删除所有这些,但离开它没有任何区别。

查找父进程 id(解析 的输出screen -ls)的方法可能不是最好的,您也可以使用psutils.process_iter(). 但这似乎有效。

于 2013-04-05T18:41:32.293 回答
2

假设您想要在屏幕中运行的进程的 PID,我在此站点的另一个问题中回答了这个问题。这是该答案的内容:

您可以在这里获取屏幕会话的 PID,如下所示:

$ 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.

让我们假设您希望在foo_Monitor屏幕会话中运行在 Bash 中的程序的 PID。使用foo_Monitor屏幕会话的 PIDbash通过搜索已知 PID 的 PPID(父 PID)来获取在其中运行的会话的 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

现在只获取bash会话的 PID:

$ ps -el | grep 1814 | grep bash | awk '{print $4}'
1815

现在我们想要具有PID 的进程。只需嵌套命令,这次使用-v标志 ongrep bash来获取不是bash 的进程:

echo $(ps -el | grep $(ps -el | grep 1814 | grep bash | awk '{print $4}') | grep -v bash | awk '{print $4}')
23869

只需将 1814 替换为真实的 PID 或您的屏幕会话:

echo $(ps -el | grep $(ps -el | grep SCREEN_SESSION_PID | grep bash | awk '{print $4}') | grep -v bash | awk '{print $4}')
于 2015-02-08T14:45:37.883 回答
1

您必须将您的 java 命令包装到获取并返回 pid 的 shell 脚本中。有关示例,请参见http://ss64.org/viewtopic.php?id=1495 。

于 2013-04-05T18:21:07.910 回答