HOWTO · C

C에서 waitpid 함수 사용

이 기사에서는 C에서 waitpid 함수를 사용하는 방법을 소개합니다.

이 페이지의 내용

부모 프로세스가 특정 자식이나 조건에 맞는 자식의 상태를 회수해야 할 때 waitpid를 사용합니다. 인수로 대상 자식과 보고할 상태 변화를 선택할 수 있습니다. 반환된 상태는 종료 코드로 바로 취급하지 말고 <sys/wait.h>의 매크로로 해석해야 합니다.

waitpid로 특정 자식 프로세스 기다리기

프로세스는 fork로 자식 프로세스를 만들 수 있습니다. 자식은 fork가 호출된 지점부터 실행을 이어가고, 부모는 자식의 프로세스 ID를 받습니다. 자식이 종료되면 부모는 wait 계열 함수를 호출해 종료 상태를 회수해야 합니다. 그래야 종료 상태를 확인하고 자식 프로세스를 회수할 수 있습니다.

호출 형식은 waitpid(pid, &status, options)입니다. 양수 pid는 해당 프로세스를 선택합니다. -1은 옵션에 부합하는 임의의 자식을 선택하며, 이는 wait에서 사용하는 선택 방식입니다. 0은 호출자와 같은 프로세스 그룹의 자식들을 선택하고, -1보다 작은 값은 pid의 절댓값을 ID로 사용하는 프로세스 그룹의 자식들을 선택합니다. 따라서 waitpid는 특정 자식을 지정할 때 유용하지만, 자식을 기다리는 wait 계열 함수가 이것뿐인 것은 아닙니다.

두 번째 인수는 자식 상태가 보고될 때 커널이 인코딩된 상태를 저장할 정수를 가리킵니다. 상태가 필요하다면 유효한 포인터를 전달하세요. 세 번째 인수는 옵션 비트 마스크입니다. 옵션을 0으로 지정하면 선택한 자식이 종료될 때까지 기다립니다. 조건에 맞는 상태 변화가 아직 없으면 호출은 블록됩니다.

보고할 자식 상태 변화 선택하기

WNOHANG을 지정하면 호출이 논블로킹으로 동작합니다. 선택한 자식에 보고할 상태 변화가 없으면 waitpid는 즉시 0을 반환합니다. 따라서 부모는 다른 작업을 계속한 뒤 나중에 다시 확인할 수 있습니다. 양수 반환값은 상태가 기록된 자식의 프로세스 ID입니다. WUNTRACED를 지정하면 아직 보고되지 않은 정지 상태의 자식도 보고하고, WCONTINUED를 지정하면 정지 후 재개된 자식도 보고합니다. 이 옵션들은 한 번의 호출을 만족시킬 수 있는 상태 변화를 지정하며, 한 번의 호출이 이후의 모든 전이를 계속 감시하는 것은 아닙니다. 비트 OR 연산자로 옵션을 결합할 수 있습니다. 예를 들면 WUNTRACED | WCONTINUED와 같습니다.

waitpid가 반환한 상태 해석하기

상태 정수는 인코딩되어 있으므로 해당 매크로로 검사해야 합니다. WIFEXITED(status)는 정상 종료인지 확인하고, 정상 종료라면 WEXITSTATUS(status)로 종료 코드를 가져옵니다. WIFSIGNALED(status)는 시그널에 의한 종료인지 확인하며, WTERMSIG(status)는 해당 시그널을 알려 줍니다. 정지한 자식을 보고하도록 요청한 경우 WIFSTOPPED(status)와 WSTOPSIG(status)로 정지 상태를 확인할 수 있습니다. 지원되는 환경에서 해당 상태를 요청했다면 WIFCONTINUED(status)로 재개된 자식인지 확인합니다. WCOREDUMP는 일부 시스템에서만 제공되므로 사용하는 코드에서는 전처리기 조건으로 사용 가능 여부를 확인해야 합니다. 이 매크로들은 <sys/wait.h>에 선언되어 있습니다.

WIFEXITED가 참일 때만 WEXITSTATUS를 호출하고, WIFSIGNALED가 참일 때만 WTERMSIG를 호출하세요. 상태 정수 자체가 자식의 종료 코드는 아닙니다. 매크로를 사용하면 구현마다 달라질 수 있는 비트 배치에 의존하지 않아도 됩니다.

반환값과 오류 처리하기

waitpid가 0보다 큰 값을 반환하면 상태를 회수한 자식의 프로세스 ID입니다. 0은 조건에 맞는 상태가 아직 없다는 뜻이며, WNOHANG을 사용했을 때 반환됩니다. -1은 오류를 나타내므로 errno를 확인해 원인을 파악하세요. 예를 들어 ECHILD는 선택 조건에 맞는 자식이 없다는 뜻이고, EINTR은 잡힌 시그널이 대기를 중단했다는 뜻입니다. EINTR 이후에도 계속 기다려야 한다면 호출을 다시 시도하세요. 그렇지 않다면 애플리케이션의 시그널 처리 정책에 따라 중단을 처리합니다. WNOHANG 없이 호출하면 선택한 자식이 계속 실행 중이고 요청한 상태 변화도 발생하지 않을 때 무기한 블록될 수 있습니다.

완전한 자식 프로세스 예제 실행하기

이 POSIX 예제는 자식 하나를 만들고, 그 자식의 종료를 지정해 기다립니다. 시그널로 대기가 중단되면 다시 시도하고, 해석한 종료 상태를 출력합니다. fork와 waitpid를 사용하며 별도의 프로그램이나 대화형 입력에 의존하지 않습니다. 이 코드는 C 컴파일러가 있는 POSIX 환경을 대상으로 하며, ISO C에서 이식 가능하다고 제시하는 예제는 아닙니다. 제공된 실행 증거에는 종료 코드 0으로 성공적으로 실행되고 아래 출력이 표시된 것으로 기록되어 있습니다.

다음 예제를 실행하세요:

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void) {
    pid_t child = fork();
    if (child == -1) {
        perror("fork");
        return EXIT_FAILURE;
    }

    if (child == 0) {
        _exit(7);
    }

    int status;
    pid_t result;
    do {
        result = waitpid(child, &status, 0);
    } while (result == -1 && errno == EINTR);

    if (result == -1) {
        perror("waitpid");
        return EXIT_FAILURE;
    }

    if (WIFEXITED(status)) {
        printf("child exit status: %d\n", WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        printf("child terminated by signal: %d\n", WTERMSIG(status));
    }

    return EXIT_SUCCESS;
}
child exit status: 7