Подробное введение в API процесса Java9

Java задняя часть API Windows
Подробное введение в API процесса Java9

Официально представлен новый процесс API в JEP 102 для улучшенияjava.lang.Processкласс, и представитьjava.lang.ProcessHandleи его вложенные интерфейсыInfoЧтобы позволить разработчикам избежать дилеммы, связанной с необходимостью время от времени использовать собственный код для получения PID собственного процесса. В этой статье подробно описаны эти новые функции.

一张逼格满满的图

1. ProcessHandle и ProcessHandle.Info

Java 9 абстрактнаProcessВ классе добавлено множество новых методов, с помощью которых можно идентифицировать непосредственные дочерние процессы и все процессы-потомки, получить PID процесса, получить снимок процесса, получитьCompletableFutureЭкземпляр для получения асинхронных уведомлений об окончании процесса и получения дополнительных возможностей:

Stream<ProcessHandle> children()
Stream<ProcessHandle> descendants()
long getPid()
ProcessHandle.Info info()
CompletableFuture<Process> onExit()
boolean supportsNormalTermination()
ProcessHandle toHandle()

Видно, что более половины методов требуют сочетанияProcessHandleинтерфейс для использования,ProcessHandleИнтерфейсы могут идентифицировать локальные процессы и управлять ими. Например,toHandle()метод может возвращатьProcessHandleКонкретный объект реализации и связанный с ним процесс,ProcessHandleМетод, объявленный в, выглядит следующим образом:

static Stream<ProcessHandle> allProcesses()
Stream<ProcessHandle> children()
int compareTo(ProcessHandle other)
static ProcessHandle current()
Stream<ProcessHandle> descendants()
boolean destroy()
boolean destroyForcibly()
long getPid()
ProcessHandle.Info info()
boolean isAlive()
static Optional<ProcessHandle> of(long pid)
CompletableFuture<ProcessHandle> onExit()
Optional<ProcessHandle> parent()
boolean supportsNormalTermination()

ProcessМетод в имени метода вызываетсяtoHandle()метод, делегированныйProcessHandleинтерфейс. Например, вызовgetPid()это вызовtoHandle().getPid(),передачаinfo()это вызовtoHandle().info(), который возвращаетProcessHandle.Infoобъект, его вложенный интерфейсInfoПредоставляет следующий список методов:

Optional<String[]> arguments()
Optional<String> command()
Optional<String> commandLine()
Optional<Instant> startInstant()
Optional<Duration> totalCpuDuration()
Optional<String> user()

каждый метод возвращаетjava.util.OptionalЭкземпляры, которые могут быть нулевыми или ненулевыми ссылками на объекты, мы все знаем, что этого можно эффективно избежать.java.lang.NullPointerException. Ниже вы узнаете больше об этих методах.

2. Получить ПИД

Processизlong getPid()Метод возвращает PID определенного процесса. Причина, по которой возвращаемое значение имеет тип long вместо int, заключается в том, что PID является целым числом без знака. Наибольшее положительное целое число равно примерно 2 миллионам, но система Linux может содержать около 4 миллионов идентификаторов PID.

Вот как получить PID:

import java.io.IOException;

public class ProcessDemo
{
   public static void main(String[] args) throws IOException
   {
      Process p = new ProcessBuilder("notepad.exe").start();
      System.out.println(p.getPid());
   }
}

java.lang.ProcessBuilderКласс (представленный в Java 5) создает процесс для программы Windows notepad.exe. использоватьstart()Метод открывается и возвращаетProcessобъект для взаимодействия с новым процессом. а потом позвониgetPid()метод получения PID.

Как создавать новые процессы

До Java 5 единственным способом создать новый процесс было использованиеRuntime.getRuntime().exec(), и теперь лучше использоватьProcessBuilder.

Скомпилируйте ProcessDemo.java:

javac ProcessDemo.java

Запустите ProcessDemo.java:

java ProcessDemo

Вы можете увидеть новый процесс в диспетчере процессовnotepad.exeработает и может видеть его PID.

9480 或者其他无符号整型

Получить PID из дескриптора процесса

если естьProcessHandleобъект, доступ к которому можно получить, вызвавgetPid()чтобы получить PID.

Вам должно быть интересно, что произойдет, если процесс не запустится или неожиданно завершится при вызове этого метода.start()бросатьjava.io.IOException, второй случай,getPid()Продолжайте возвращать PID после завершения процесса.

3. Получите информацию о процессе

ProcessHandle.InfoОпределены некоторые методы, возвращающие информацию о процессе, такую ​​как путь к исполняемому файлу процесса, время запуска процесса, имя пользователя, запустившего процесс, и т. д.

Следующий код запускает процесс и выводит некоторую информацию о процессе:

import java.io.IOException;

import java.time.Duration;
import java.time.Instant;

public class ProcessDemo
{
   public static void main(String[] args) 
      throws InterruptedException, IOException
   {
      dumpProcessInfo(ProcessHandle.current());
      Process p = new ProcessBuilder("notepad.exe", "C:\\temp\\names.txt").start();
      dumpProcessInfo(p.toHandle());
      p.waitFor();
      dumpProcessInfo(p.toHandle());
   }

   static void dumpProcessInfo(ProcessHandle ph)
   {
      System.out.println("PROCESS INFORMATION");
      System.out.println("===================");
      System.out.printf("Process id: %d%n", ph.getPid());
      ProcessHandle.Info info = ph.info();
      System.out.printf("Command: %s%n", info.command().orElse(""));
      String[] args = info.arguments().orElse(new String[]{});
      System.out.println("Arguments:");
      for (String arg: args)
         System.out.printf("   %s%n", arg);
      System.out.printf("Command line: %s%n", info.commandLine().orElse(""));
      System.out.printf("Start time: %s%n", 
                        info.startInstant().orElse(Instant.now()).toString());
      System.out.printf("Run time duration: %sms%n",
                        info.totalCpuDuration()
                            .orElse(Duration.ofMillis(0)).toMillis());
      System.out.printf("Owner: %s%n", info.user().orElse(""));
      System.out.println();
   }
}

существуетmain()метод вызывается первымProcessHandle.current()чтобы получить дескриптор текущего процесса, затем используйтеdumpProcessInfo()метод для вывода подробной информации о процессе создания дампа. Следующий стартnotepad.exe, и дамп его информации о процессе. Подожди покаnotepad.exeПосле завершения он снова сбрасывает свою информацию.

dumpProcessInfo()Метод сначала выводит идентификационную информацию заголовка, затем выводит PID, а затем получаетProcessHandle.InfoЦитировать. Далее звонитеcommand()и другиеInfoметоды, выведите их значения. Если метод возвращает null (поскольку информация недоступна), передатьOptionalизorElse()способ возврата информации.

Вот вывод, во время которого видно окно блокнота:

PROCESS INFORMATION
===================
Process id: 1140
Command: C:\PROGRA~1\Java\jdk-9\bin\java.exe
Arguments:
Command line: 
Start time: 2017-03-02T22:24:40.998Z
Run time duration: 890ms
Owner: jeff\jeffrey

PROCESS INFORMATION
===================
Process id: 5516
Command: C:\Windows\System32\notepad.exe
Arguments:
Command line: 
Start time: 2017-03-02T22:24:41.763Z
Run time duration: 0ms
Owner: jeff\jeffrey

PROCESS INFORMATION
===================
Process id: 5516
Command: 
Arguments:
Command line: 
Start time: 2017-03-02T22:24:41.763Z
Run time duration: 234ms
Owner: jeff\jeffrey

ИНФОРМАЦИЯ О ПРОЦЕССЕ в третьей части не появлялась, пока не исчез интерфейс блокнота. Метод arguments() из Info не печатает командную строку в файл C:\temp\names.txt либо потому, что информация в данный момент недоступна, либо из-за ошибки. После завершения процесса метод command() возвращает null. Наконец, когда метод command() или метод arguments() возвращает значение null, метод commandLine() также возвращает значение null.

4. Получить информацию обо всех процессах

ProcessHandleсерединаallProcesses()Метод возвращает все видимые дескрипторы процессов в текущей системе наподобие Stream API в Java8. В следующем коде показано, как использовать Stream для получения дескриптора процесса, получения первых четырех процессов и вывода их информации.

import java.io.IOException;

import java.time.Duration;
import java.time.Instant;

public class ProcessDemo
{
   public static void main(String[] args)
   {
      ProcessHandle.allProcesses()
                   .filter(ph -> ph.info().command().isPresent())
                   .limit(4)
                   .forEach((process) -> dumpProcessInfo(process));
   }

   static void dumpProcessInfo(ProcessHandle ph)
   {
      System.out.println("PROCESS INFORMATION");
      System.out.println("===================");
      System.out.printf("Process id: %d%n", ph.getPid());
      ProcessHandle.Info info = ph.info();
      System.out.printf("Command: %s%n", info.command().orElse(""));
      String[] args = info.arguments().orElse(new String[]{});
      System.out.println("Arguments:");
      for (String arg: args)
         System.out.printf("   %s%n", arg);
      System.out.printf("Command line: %s%n", info.commandLine().orElse(""));
      System.out.printf("Start time: %s%n", 
                        info.startInstant().orElse(Instant.now()).toString());
      System.out.printf("Run time duration: %sms%n",
                        info.totalCpuDuration()
                            .orElse(Duration.ofMillis(0)).toMillis());
      System.out.printf("Owner: %s%n", info.user().orElse(""));
      System.out.println();
   }
}

Метод allProcesses() вызывается в методе main() для явного импорта потока дескриптора потока из имени пути в фильтр, а затем обертывания его в новый поток дескриптора потока. (В моей среде путь не распечатывается, когда процесс завершается) Метод limit(4) может перехватить не более 4 процессов, чтобы поместить их в поток. Наконец, повторите всю их информацию.

Вот результат:

PROCESS INFORMATION
===================
Process id: 8036
Command: C:\Windows\explorer.exe
Arguments:
Command line: 
Start time: 2017-03-02T16:21:14.436Z
Run time duration: 299328ms
Owner: jeff\jeffrey

PROCESS INFORMATION
===================
Process id: 10200
Command: C:\Windows\System32\dllhost.exe
Arguments:
Command line: 
Start time: 2017-03-02T16:21:16.255Z
Run time duration: 2000ms
Owner: jeff\jeffrey

PROCESS INFORMATION
===================
Process id: 1544
Command: C:\Program Files (x86)\WNSS\WNSS.exe
Arguments:
Command line: 
Start time: 2017-03-02T16:21:21.708Z
Run time duration: 862375ms
Owner: jeff\jeffrey

PROCESS INFORMATION
===================
Process id: 8156
Command: C:\Users\jeffrey\AppData\Local\SweetLabs App Platform\Engine\ServiceHostAppUpdater.exe
Arguments:
Command line: 
Start time: 2017-03-02T16:21:24.302Z
Run time duration: 2468ms
Owner: jeff\jeffrey

Метод Children() и метод потомков() ProcessHandle очень похожи на метод allProcesses() f, за исключением того, что они являются статическими методами, а тип возвращаемого значения отличается, между ними существует установленная принадлежность, Children() — это A подмножество потомков (), потомки () являются подмножеством всех процессов ().

5. Триггерный механизм завершения процесса

Наконец, метод onExit() класса ProcessHandle возвращает java.util.concurrent.CompletableFuture, что позволяет процессу выполнять синхронные или асинхронные операции после завершения.

Распечатайте PID процесса после его завершения:

import java.io.IOException;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class ProcessDemo
{
   public static void main(String[] args) 
      throws ExecutionException, InterruptedException, IOException
   {
      Process p = new ProcessBuilder("notepad.exe").start();
      ProcessHandle ph = p.toHandle();
      CompletableFuture<ProcessHandle> onExit = ph.onExit();
      onExit.get();
      onExit.thenAccept(ph_ -> System.out.printf("PID %d terminated%n", ph_.getPid()));
   }
}

Метод main() сначала создает процесс notepad.exe. Впоследствии получается дескриптор процесса, и с этим дескриптором получается CompletableFuture. onExit.get() выполнит некоторые операции после того, как main() получит сигнал завершения процесса.

PID 7460 terminated

6. Заключение

Улучшения Process API в Java 9 давно назрели и приветствуются в Java. Хотя в этой статье представлены некоторые из его API, вам нужно изучить больше, например, использование метода supportsNormalTermination() или parent() и т. д.