web-dev-qa-db-ja.com

Javaプログラム内で開始したばかりのプロセスのPIDを取得する方法?

私は次のコードでプロセスを開始しました

 ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
 try {
     Process p = pb.start();       
 } 
 catch (IOException ex) {}

ここで、開始したばかりのプロセスのPIDを知る必要があります。

66
raf

このためのパブリックAPIはまだありません。サンを参照してください バグ4244896 、サン バグ4250622

回避策として:

Runtime.exec(...)

タイプのオブジェクトを返します

Java.lang.Process

Processクラスは抽象的であり、返されるのは、オペレーティングシステム用に設計されたProcessのサブクラスです。たとえば、Macでは、Java.lang.UnixProcesspidというプライベートフィールドがあります。 Reflectionを使用すると、このフィールドの値を簡単に取得できます。これは明らかにハッキングですが、役立つかもしれません。とにかくPIDは何が必要ですか?

25
Amir Afghani

このページにはHOWTOがあります:

http://www.golesny.de/p/code/javagetpid

Windowsの場合:

Runtime.exec(..)

「Java.lang.Win32Process」のインスタンスを返します)OR "Java.lang.ProcessImpl"

両方にプライベートフィールド「ハンドル」があります。

これは、プロセスのOSハンドルです。 PIDを照会するには、この+ Win32 APIを使用する必要があります。そのページには、その方法の詳細が記載されています。

24
Shamit Verma

Java 9クラスProcessには新しいメソッド long pid() があるため、シンプルな

ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
    Process p = pb.start();
    long pid = p.pid();      
} catch (IOException ex) {
    // ...
}
23
czerny

Unixシステム(LinuxおよびMac)

 public static synchronized long getPidOfProcess(Process p) {
    long pid = -1;

    try {
      if (p.getClass().getName().equals("Java.lang.UNIXProcess")) {
        Field f = p.getClass().getDeclaredField("pid");
        f.setAccessible(true);
        pid = f.getLong(p);
        f.setAccessible(false);
      }
    } catch (Exception e) {
      pid = -1;
    }
    return pid;
  }
19
LRBH10

ライブラリに jna (「JNA」と「JNA Platform」の両方)を含め、この関数を使用します。

import com.Sun.jna.Pointer;
import com.Sun.jna.platform.win32.Kernel32;
import com.Sun.jna.platform.win32.WinNT;
import Java.lang.reflect.Field;

public static long getProcessID(Process p)
    {
        long result = -1;
        try
        {
            //for windows
            if (p.getClass().getName().equals("Java.lang.Win32Process") ||
                   p.getClass().getName().equals("Java.lang.ProcessImpl")) 
            {
                Field f = p.getClass().getDeclaredField("handle");
                f.setAccessible(true);              
                long handl = f.getLong(p);
                Kernel32 kernel = Kernel32.INSTANCE;
                WinNT.HANDLE hand = new WinNT.HANDLE();
                hand.setPointer(Pointer.createConstant(handl));
                result = kernel.GetProcessId(hand);
                f.setAccessible(false);
            }
            //for unix based operating systems
            else if (p.getClass().getName().equals("Java.lang.UNIXProcess")) 
            {
                Field f = p.getClass().getDeclaredField("pid");
                f.setAccessible(true);
                result = f.getLong(p);
                f.setAccessible(false);
            }
        }
        catch(Exception ex)
        {
            result = -1;
        }
        return result;
    }

JNAは here から、JNA Platformは here からダウンロードすることもできます。

14
arcsin

私は、ほとんどのプラットフォームで作業している間、まったく防弾に見えるソリューションを見つけたと思います。アイデアは次のとおりです。

  1. 新しいプロセスを生成する/プロセスを殺す前に取得するJVM全体のミューテックスを作成する
  2. プラットフォーム依存コードを使用して、子プロセスのリスト+ JVMプロセスのPIDを取得します
  3. 新しいプロセスを生成する
  4. 子プロセス+ pidの新しいリストを取得し、前のリストと比較します。新しいのはあなたの男です。

子プロセスのみをチェックするため、同じマシン内の他のプロセスに誤用されることはありません。 JVM全体のミューテックスにより、新しいプロセスが正しいものであることを確認できます。

子プロセスリストの読み取りは、WindowsでWIN API呼び出しを必要としないため、プロセスオブジェクトからPIDを取得するよりも簡単です。さらに重要なことは、いくつかのライブラリで既に行われていることです。

以下は、 JavaSysMon ライブラリを使用した上記のアイデアの実装です。それ

class UDKSpawner {

    private int uccPid;
    private Logger uccLog;

    /**
     * Mutex that forces only one child process to be spawned at a time. 
     * 
     */
    private static final Object spawnProcessMutex = new Object();

    /**
     * Spawns a new UDK process and sets {@link #uccPid} to it's PID. To work correctly,
     * the code relies on the fact that no other method in this JVM runs UDK processes and
     * that no method kills a process unless it acquires lock on spawnProcessMutex.
     * @param procBuilder
     * @return 
     */
    private Process spawnUDK(ProcessBuilder procBuilder) throws IOException {
        synchronized (spawnProcessMutex){            
            JavaSysMon monitor = new JavaSysMon();
            DirectUDKChildProcessVisitor beforeVisitor = new DirectUDKChildProcessVisitor();
            monitor.visitProcessTree(monitor.currentPid(), beforeVisitor);
            Set<Integer> alreadySpawnedProcesses = beforeVisitor.getUdkPids();

            Process proc = procBuilder.start();

            DirectUDKChildProcessVisitor afterVisitor = new DirectUDKChildProcessVisitor();
            monitor.visitProcessTree(monitor.currentPid(), afterVisitor);
            Set<Integer> newProcesses = afterVisitor.getUdkPids();

            newProcesses.removeAll(alreadySpawnedProcesses);

            if(newProcesses.isEmpty()){
                uccLog.severe("There is no new UKD PID.");
            }
            else if(newProcesses.size() > 1){
                uccLog.severe("Multiple new candidate UDK PIDs");
            } else {
                uccPid = newProcesses.iterator().next();
            }
            return proc;
        }
    }    

    private void killUDKByPID(){
        if(uccPid < 0){
            uccLog.severe("Cannot kill UCC by PID. PID not set.");
            return;
        }
        synchronized(spawnProcessMutex){
            JavaSysMon monitor = new JavaSysMon();
            monitor.killProcessTree(uccPid, false);
        }
    }

    private static class DirectUDKChildProcessVisitor implements ProcessVisitor {
        Set<Integer> udkPids = new HashSet<Integer>();

        @Override
        public boolean visit(OsProcess op, int i) {
            if(op.processInfo().getName().equals("UDK.exe")){
                udkPids.add(op.processInfo().getPid());
            }
            return false;
        }

        public Set<Integer> getUdkPids() {
            return udkPids;
        }
    }
}
8
Martin Modrák

私のテストでは、すべてのIMPLクラスに「pid」フィールドがありました。これは私のために働いています:

public static int getPid(Process process) {
    try {
        Class<?> cProcessImpl = process.getClass();
        Field fPid = cProcessImpl.getDeclaredField("pid");
        if (!fPid.isAccessible()) {
            fPid.setAccessible(true);
        }
        return fPid.getInt(process);
    } catch (Exception e) {
        return -1;
    }
}

戻り値が-1でないことを確認してください。そうであれば、psの出力を解析します。

3
Jared Rummler

非常に簡単なProcessオブジェクトからUNIX PIDを取得するために、移植性のないアプローチを使用しました。

ステップ1:いくつかのReflection API呼び出しを使用して、ターゲットサーバーJRE上のProcess実装クラスを識別します(Processは抽象クラスであることに注意してください)。 UNIX実装が私の実装に似ている場合、プロセスのPIDを含むpidという名前のプロパティを持つ実装クラスが表示されます。これが、私が使用したロギングコードです。

    //--------------------------------------------------------------------
    // Jim Tough - 2014-11-04
    // This temporary Reflection code is used to log the name of the
    // class that implements the abstract Process class on the target
    // JRE, all of its 'Fields' (properties and methods) and the value
    // of each field.
    //
    // I only care about how this behaves on our UNIX servers, so I'll
    // deploy a snapshot release of this code to a QA server, run it once,
    // then check the logs.
    //
    // TODO Remove this logging code before building final release!
    final Class<?> clazz = process.getClass();
    logger.info("Concrete implementation of " + Process.class.getName() +
            " is: " + clazz.getName());
    // Array of all fields in this class, regardless of access level
    final Field[] allFields = clazz.getDeclaredFields();
    for (Field field : allFields) {
        field.setAccessible(true); // allows access to non-public fields
        Class<?> fieldClass = field.getType();
        StringBuilder sb = new StringBuilder(field.getName());
        sb.append(" | type: ");
        sb.append(fieldClass.getName());
        sb.append(" | value: [");
        Object fieldValue = null;
        try {
            fieldValue = field.get(process);
            sb.append(fieldValue);
            sb.append("]");
        } catch (Exception e) {
            logger.error("Unable to get value for [" +
                    field.getName() + "]", e);
        }
        logger.info(sb.toString());
    }
    //--------------------------------------------------------------------

STEP 2: Reflectionロギングから取得した実装クラスとフィールド名に基づいて、Process実装クラスをスリするコードを記述し、Reflection APIを使用してそこからPIDを取得します。以下のコードは、UNIXのフレーバーで機能します。 EXPECTED_IMPL_CLASS_NAMEおよびEXPECTED_PID_FIELD_NAME定数を調整して、機能させる必要がある場合があります。

/**
 * Get the process id (PID) associated with a {@code Process}
 * @param process {@code Process}, or null
 * @return Integer containing the PID of the process; null if the
 *  PID could not be retrieved or if a null parameter was supplied
 */
Integer retrievePID(final Process process) {
    if (process == null) {
        return null;
    }

    //--------------------------------------------------------------------
    // Jim Tough - 2014-11-04
    // NON PORTABLE CODE WARNING!
    // The code in this block works on the company UNIX servers, but may
    // not work on *any* UNIX server. Definitely will not work on any
    // Windows Server instances.
    final String EXPECTED_IMPL_CLASS_NAME = "Java.lang.UNIXProcess";
    final String EXPECTED_PID_FIELD_NAME = "pid";
    final Class<? extends Process> processImplClass = process.getClass();
    if (processImplClass.getName().equals(EXPECTED_IMPL_CLASS_NAME)) {
        try {
            Field f = processImplClass.getDeclaredField(
                    EXPECTED_PID_FIELD_NAME);
            f.setAccessible(true); // allows access to non-public fields
            int pid = f.getInt(process);
            return pid;
        } catch (Exception e) {
            logger.warn("Unable to get PID", e);
        }
    } else {
        logger.warn(Process.class.getName() + " implementation was not " +
                EXPECTED_IMPL_CLASS_NAME + " - cannot retrieve PID" +
                " | actual type was: " + processImplClass.getName());
    }
    //--------------------------------------------------------------------

    return null; // If PID was not retrievable, just return null
}
2
Jim Tough

これは一般的な答えではありません。

ただし、一部のプログラム、特にサービスや実行時間の長いプログラムでは、「pidファイル」を作成(またはオプションで作成を提案)します。

たとえば、LibreOfficeは--pidfile={file}docs を参照してください。

Java/Linuxソリューションを探していましたが、PIDは(私の場合)手元にありました。

1
Ondra Žižka

簡単な解決策はありません。私が過去にやった方法は、Unixライクなシステムでpsコマンド、またはWindowsでtasklistコマンドを実行するために別のプロセスを開始し、その後の出力を解析することです。必要なPIDのコマンド。実際には、PIDを返したプラットフォームごとに個別のシェルスクリプトにそのコードを入れて、Javaをプラットフォームに依存しないようにできるようにしました。これはできません。短時間のタスクではうまく機能しますが、それは私にとっては問題ではありませんでした。

0
Stewart Murrie

GNU/Linux&MacOS(または一般的にUNIXのような)システムの場合、私は以下の方法を使用しました。

private int tryGetPid(Process process)
{
    if (process.getClass().getName().equals("Java.lang.UNIXProcess"))
    {
        try
        {
            Field f = process.getClass().getDeclaredField("pid");
            f.setAccessible(true);
            return f.getInt(process);
        }
        catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException e)
        {
        }
    }

    return 0;
}
0
csonuryilmaz

これを行う唯一の移植可能な方法は、(子)プロセスを別の(親)Javaプロセスを通して実行することです。これにより、親プロセスの実際のPIDが通知されます。子プロセス何でもかまいません。

このラッパーのコードは

package com.panayotis.wrapper;

import Java.io.File;
import Java.io.IOException;
import Java.lang.management.ManagementFactory;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
        ProcessBuilder pb = new ProcessBuilder(args);
        pb.directory(new File(System.getProperty("user.dir")));
        pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        pb.redirectError(ProcessBuilder.Redirect.INHERIT);
        pb.start().waitFor();
    }
}

これを使用するには、このファイルだけでjarファイルを作成し、コマンド引数で呼び出します:

String Java = System.getProperty("Java.home") + separator + "bin" + separator + "Java.exe";
String jar_wrapper = "path\\of\\wrapper.jar";

String[] args = new String[]{Java, "-cp", jar_wrapper, "com.panayotis.wrapper.Main", actual_exec_args...);
0
Panayotis

jnr-process プロジェクトはこの機能を提供します。

これは、jrubyで使用されるJavaネイティブランタイムの一部であり、将来のプロトタイプと見なすことができます Java-FFI

0
the8472

1つの解決策は、プラットフォームが提供する特異なツールを使用することです。

private static String invokeLinuxPsProcess(String filterByCommand) {
    List<String> args = Arrays.asList("ps -e -o stat,pid,unit,args=".split(" +"));
    // Example output:
    // Sl   22245 bpds-api.service                /opt/libreoffice5.4/program/soffice.bin --headless
    // Z    22250 -                               [soffice.bin] <defunct>

    try {
        Process psAux = new ProcessBuilder(args).redirectErrorStream(true).start();
        try {
            Thread.sleep(100); // TODO: Find some passive way.
        } catch (InterruptedException e) { }

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(psAux.getInputStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (!line.contains(filterByCommand))
                    continue;
                String[] parts = line.split("\\w+");
                if (parts.length < 4)
                    throw new RuntimeException("Unexpected format of the `ps` line, expected at least 4 columns:\n\t" + line);
                String pid = parts[1];
                return pid;
            }
        }
    }
    catch (IOException ex) {
        log.warn(String.format("Failed executing %s: %s", args, ex.getMessage()), ex);
    }
    return null;
}

免責事項:テストされていませんが、あなたはアイデアを得る:

  • psを呼び出してプロセスをリストし、
  • あなたがそれを起動したコマンドを知っているのであなたのものを見つけてください。
  • 同じコマンドで複数のプロセスがある場合、次のことができます。
    • 別の仮引数を追加してそれらを区別します
    • 増加するPIDに依存する(実際には安全ではなく、同時ではない)
    • プロセス作成の時間を確認します(実際には区別するには粗すぎて、同時ではありません)
    • 特定の環境変数を追加し、psでリストします。
0
Ondra Žižka

移植性が問題にならず、Windowsのすべての最新バージョンで動作することがテスト済みであることがわかっているコードを使用しながら、Windowsでpidを手間をかけずに取得したい場合は、kohsukeの winp を使用できます。 =ライブラリ。 Maven Centralでも簡単に使用できます。

Process process = //...;
WinProcess wp = new WinProcess(process);
int pid = wp.getPid();
0
allquixotic

そのような機能を備えたオープンソースのライブラリがあり、クロスプラットフォームの実装があります: https://github.com/OpenHFT/Java-Thread-Affinity

PIDを取得するだけではやり過ぎかもしれませんが、CPUやスレッドID、特にスレッドアフィニティなどの他のものが必要な場合は、それで十分かもしれません。

現在のスレッドのPIDを取得するには、Affinity.getAffinityImpl().getProcessId()を呼び出すだけです。

これは、JNAを使用して実装されます(arcsinの回答を参照)。

0
Luciano