Programming/Java

Java 운영체제(윈도우, 리눅스) 프로세스 상태 확인하는 방법

Jan92 2024. 2. 11. 00:17

Java 운영체제 프로세스 상태 확인 방법(Process, ProcessBuilder)

java.lang.Process

최근 Java 코드를 사용하여 서버의 특정 포트를 사용하는 프로세스의 상태를 확인하는 기능이 필요했는데요.

'java.lang.Process', 'java.lang.ProcessBuilder' 클래스를 사용하여 '운영체제(windows, linux)에 따른 프로세스 상태를 확인하는 방법'에 대해 정리해 보았습니다.

 

내용의 핵심은 'Process', 'ProcessBuilder'이며, Process는 Java 외부 프로세스를 실행하고 제어할 수 있기 때문에 해당 클래스를 활용하면 프로세스의 상태를 확인하는 기능뿐만 아니라 프로세스와 관련된 다양한 기능들을 구현할 수 있다는 것입니다.

 


Process Class

'java.lang.Process' 클래스는 Java 외부 프로세스(다른 프로그램이나 명령어)를 실행하고 제어하기 위한 클래스입니다.

초기 버전에서는 'Runtime.exec()' 메서드를 통해 Process 클래스의 인스턴스를 반환받아 사용했으며, JDK 1.5 버전부터는 'ProcessBuilder.start()' 방식이 더 선호되고 있습니다.

(추가로 Java 18부터 Runtime.exec() method가 지원 중단(deprecated) 되었습니다.)

 

생성된 Process 인스턴스(하위 프로세스)getInputStream(), getOutputStream(), getErrorStream() 메서드를 통해 반환된 스트림을 가지고 I/O 작업을 수행하며, 해당 작업은 상위 프로세스로 리디렉션 됩니다.

 

 

Process Class 공식 문서 내용 중 일부

공식 문서에 따른 주의할 점으로는 플랫폼에 따라 'InputStream', 'OutputStream'에 대해 버퍼 크기 제한이 있을 수 있기 때문에 프로세스 실행 후 InputStream, OutputStream에 대한 적절한 처리가 이뤄지지 않는 경우 버퍼가 넘쳐 프로세스가 멈추거나 deadlock 상태에 빠질 수 있다는 것입니다.

 


Linux 운영체제 프로세스 상태 확인 코드

public void getLinuxProcessInfo(int targetPort) throws IOException {
    // ProcessBuilder 를 통해 프로세스 조회를 위한 명령어 실행
    Process ps = new ProcessBuilder("/bin/sh", "-c", "lsof -Pi4TCP -sTCP:LISTEN").start();
    BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));

    String line;
    while ((line = br.readLine()) != null) {
        if (line.contains(":" + targetPort)) {
            // 두 자리 이상의 공백을 모두 한 자리의 공백으로 replace
            while (line.contains("  ")) {
                line = line.replaceAll("  ", " ");
            }

            // contains 메서드로 인해 조회된 port 가 targetPort 와 동일한지 확인 필요 (ex. 3306, 33060)
            String searchedPort = line.split(" ")[8];
            searchedPort = searchedPort.substring(searchedPort.indexOf(":") + 1);

            if (searchedPort.equals(String.valueOf(targetPort))) {
                System.out.println("processInfo: " + line);
            }
        }
    }
}

리눅스 운영체제에서 프로세스 상태를 확인하기 위한 코드 예시입니다.

 

ProcessBuilder(List<String> command), ProcessBuilder(String... command) 다음 두 가지 생성자을 통해 ProcessBuilder를 생성할 수 있으며, 'start()' method를 통해 프로세스 조회를 위한 명령어를 실행합니다.

 

 

lsof 명령어를 통한 프로세스 조회

'/bin/sh'를 통해 'lsof -Pi4TCP -sTCP:LISTEN'라는 명령어를 실행했으며, 여기서 '-c' 옵션은 쉘(Shell)에서 파일이 아닌 명령어를 실행하도록 지시하는 역할을 합니다.

그리고 프로세스에 실행된 명령어에 대한 InputStream을 가져와서 BufferedReader를 통해 결과를 한 줄씩 읽어 필요한 과정을 처리하게 됩니다.

 

* 여기서 'lsof -Pi4TCP -sTCP:LISTEN' 명령어를 사용했지만, 기능에 따라 필요한 명령어를 사용하고 'readLine()'을 통해 얻은 line에 대한 처리를 하도록 유연하게 사용할 수 있습니다.

* Java 9 버전부터는 Process 객체에 대한 pid를 확인할 수 있는 'pid()' 메서드가 제공됩니다.

 

 

// 필요한 작업이 끝나고 프로세스가 살아 있을 경우 처리
if (ps.isAlive()) {
    ps.destroy();
}

추가적으로 위 예시에서는 명령어가 실행되고 해당 프로세스가 자동으로 종료되기 때문에 문제가 없지만, 만약 프로세스를 직접 종료해줘야 하는 경우 다음과 같이 'isAlive()', 'destroy()' 메서드를 통해 프로세스를 종료해 주는 로직이 필요할 수 있습니다.

 

 


Windows 운영체제 프로세스 상태 확인 코드

// ProcessBuilder 변경된 부분의 코드만
Process ps = new ProcessBuilder("cmd", "/c", "netstat -ano -p TCP | find \"LISTEN\"").start();

이어서 윈도우 운영체제에서 프로세스를 확인하기 위한 코드입니다.

 

바뀐 코드 내용은 ProcessBuilder의 command 값이 윈도우 운영체제에 따른 명령어로 바뀌었으며, while 문 안에서 'line.split(" ")' 부분을 통해 조회된 프로세스 정보 중 port를 가져오는 부분의 위치가 바뀌었기 때문에 해당 부분의 코드가 변경되었습니다.

 

 

netstat 명령어를 통한 프로세스 조회

 

boolean isWindows = System.getProperty("os.name").toLowerCase().startWith("windows");

추가로 런타임 시 시스템 운여체제를 파악하여 운영체제에 따른 명령어를 실행해야 하는 경우 다음과 같이 System 클래스에서 'os.name' 속성을 읽어 시스템의 운영체제를 파악하여 분기처리를 할 수도 있습니다.

 

 

 

< 관련 참고 자료 >

https://www.baeldung.com/java-process-api
https://d2.naver.com/helloworld/1113548