次のコードでプロセスを開始しました
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
Process p = pb.start();
}
catch (IOException ex) {}
ここで、開始したばかりのプロセスの pid を知る必要があります。
次のコードでプロセスを開始しました
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
Process p = pb.start();
}
catch (IOException ex) {}
ここで、開始したばかりのプロセスの pid を知る必要があります。
このためのパブリック API はまだありません。Sunバグ 4244896、Sunバグ4250622 を参照してください。
回避策として:
Runtime.exec(...)
タイプのオブジェクトを返します
java.lang.Process
Process クラスは抽象的であり、オペレーティング システム用に設計された Process のサブクラスが返されます。たとえば、Mac では、java.lang.UnixProcess
というプライベート フィールドがあるものを返しますpid
。Reflection を使用すると、このフィールドの値を簡単に取得できます。これは確かにハックですが、役立つかもしれません。PID
とにかくあなたは何のために必要ですか?
このページには HOWTO があります:
http://www.golesny.de/p/code/javagetpid
Windows の場合:
Runtime.exec(..)
"java.lang.Win32Process") または "java.lang.ProcessImpl" のインスタンスを返します
どちらもプライベート フィールド「ハンドル」を持っています。
これは、プロセスの OS ハンドルです。PID を照会するには、この + Win32 API を使用する必要があります。そのページには、その方法の詳細が記載されています。
ほとんどのプラットフォームで作業している間、非常に防弾に見える解決策を見つけたと思います。これがアイデアです:
子プロセスのみをチェックするため、同じマシン内の他のプロセスによって不当に扱われることはありません。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;
}
}
}
私のテストでは、すべての 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
。
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());
}
//--------------------------------------------------------------------
ステップ 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
}
これは一般的な答えではありません。
ただし、一部のプログラム、特にサービスや長期実行プログラムは、「pid ファイル」を作成します (または、オプションで作成を提案します)。
たとえば、LibreOffice のオファーについては、ドキュメント--pidfile={file}
を参照してください。
私は Java/Linux ソリューションをかなり探していましたが、(私の場合) PID は手元にありました。
jnr-processプロジェクトはこの機能を提供します。
これは、jruby によって使用される Java ネイティブ ランタイムの一部であり、将来のJava-FFIのプロトタイプと見なすことができます。
簡単な解決策はありません。私が過去に行った方法は、別のプロセスを開始してps
、Unix ライクなシステムでコマンドを実行するか、 tasklist
Windows でコマンドを実行し、そのコマンドの出力を解析して必要な PID を取得することでした。実際には、PID を返すプラットフォームごとに個別のシェル スクリプトにそのコードを入れることになりました。これにより、Java 部分を可能な限りプラットフォームに依存しないように保つことができました。これは短期間のタスクではうまく機能しませんが、私にとっては問題ではありませんでした。