[리눅스 시스템 프로그래밍] 7.8 프로세스 종료 대기 및 확인, wait, waitpid, wait3, wait4

리눅스 시스템에서는 프로세스의 종료를 대기하거나 종료 상태를 확인하는 wait 함수 군을 제공합니다.

#include <sys/types.h >

#include <sys/wait.h>

pid_t wait(int *staloc);

pid_t waitpid(pid_t pid, int *staloc, int options);

pid_t wait3(int *staloc, int options, struct rusage *rus);

pid_t wait4(pid_t pid, int *staloc, int options, struct rusage *rus);

리눅스 커널에서는 자식 프로세스가 종료하면 부모 프로세스에게 SIGCHLD 시그널을 보냅니다. 시그널을 이용하는 방법은 다음에 다룰 것이며 여기에서는 wait 함수군을 이용하여 자식 프로세스의 종료를 확인하고 종료 상태값을 확인하는 것을 알아볼게요.

pid_t wait(int *staloc);

wait 함수는 여러 개의 자식 프로세스 중에 어느 하나라도 종료하면 종료한 자식 프로세스 ID를 반환합니다. 만약 종료하는 프로세스가 없으면 블록 상태로 계속 기다립니다. 그리고 종료 상태값은 staloc에 저장됩니다. 만약 자식 프로세스가 없을 때에는 -1을 리턴합니다.

종료 상태값은 16비트 값으로 구성합니다. 하위 8비트 값이 0이면 상위 8비트 값은 종료할 때 exit 함수에 전달한 인자값(main 함수 return 값)입니다. 하위 8비트 값이 0x7f일 때는 자식 프로세스가 SIGTSTP이나 SIGSTOP 시그널에 의해 임시 중단 상태이며 상위 8비트에 시그널 번호가 옵니다. 비정상 종료일 때는 상위 8비트 값은 0이지만 하위 8비트 중에 7비트는 시그널 번호가 오고 맨 첫번째 비트는 코어 플래그가 옵니다. 코어 플래그 값은 코어 파일 발생 여부 정보를 의미합니다.

[그림 7.11] 종료 상태값
[그림 7.11] 종료 상태값

리눅스 시스템에서는 종료 상태값을 확인할 수 있는 몇 가지 매크로 함수를 제공합니다.

WIFEXITED(status) :정상 종료일 때 참

WEXITSTATUS(status): 정상 종료일 때 하위 8비트 값

WIFSIGNALED(status): 시그널에 의해 종료했을 때 참

WTERMSIG(status): 시그널에 의해 종료했을 때 시그널 번호

WCOREDUMP(status): 코어 파일 발생 여부

WIFSTOPPED(status): 일시 중단 상태일 때 참

WSTOPSIG(status): 일시 중단시킨 시그널 번호

다음은 다섯 개의 자식 프로세스를 생성하여 wait 시스템 함수를 통해 자식 프로세스를 대기하고 종료 상태값을 확인하는 예제 코드입니다. 예제에서는 자식 프로세스를 생성하고 자식 프로세스에서는 랜덤한 시간을 대기합니다. 그리고 일련 번호를 exit 함수의 입력 인자로 전달하여 종료합니다. wait 함수가 자식 프로세스 중에 어느 하나라도 종료하면 종료한 자식 프로세스 id를 반환하는 것을 알 수 있고 입력 인자로 전달한 메모리에 자식 프로세스의 종료 상태값을 알 수 있습니다.

/**********************************************************************
* ex_wait.c                                                           *
* exmple source – wait                                                *
**********************************************************************/

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


void MakeChild(int seq,int rval);
int main()
{
    srand((unsigned)time(0));

    int i = 0;
    for(i=0; i<5; i++)
    {
        MakeChild(i+1, rand()%10);
    }

    int rval=0;
    pid_t cpid=0;
    while(i>0)
    {
        cpid = wait(&rval);
        printf("cpid:%d exit status:%#x\n",cpid, rval);
        i--;
    }
    return 0;
}
void MakeChild(int seq,int rval)
{
    pid_t cpid = fork();

    if(cpid == -1)
    {
        perror("error fork");
        return;
    }

    if(cpid>0)
    {
        printf("fork child pid:%u\n",cpid);
    }
    else
    {
        printf("pid:%d sleep %dseconds\n",getpid(), rval);
        sleep(rval);
[그림 7.12] ex_wait 실행 화면
[그림 7.12] ex_wait 실행 화면

pid_t waitpid(pid_t pid, int *staloc, int options);

waitpid 함수는 프로세스 id를 첫 번째 인자로 전달할 수 있으며 이 값에 따라 대기할 프로세스는 달라집니다. 양수는 해당 pid값을 갖는 자식 프로세스를 기다립니다. 0일 때는 자신과 같은 그룹에 있는 프로세스의 자식 프로세스의 종료를 기다립니다. -1일 때는 모든 하위 프로세스를 기다리며 -1보다 작은 값일 때는 절대값과 같은 자식 프로세스의 종료를 기다립니다.

그리고 세 번째 인자로 옵션을 전달할 수 있으며 WNOHANG일 때는 종료를 기다리지 않고 바로 리턴합니다. 이는 이미 종료한 프로세스의 종료 상태값을 확인하기 위한 목적으로 사용합니다. 그리고 WUNTRACED일 때는 종료 뿐만 아니라 멈춤일 때에도 반환합니다.

pid_t wait3(int *staloc, int options, struct rusage *rus);

pid_t wait4(pid_t pid, int *staloc, int options, struct rusage *rus);

wait3, wait4 함수는 사용자 시간 정보를 확인할 수 있습니다. 다음은 wait3 함수를 이용하여 자식 프로세스의 종료를 대기한 후 자원 사용량을 출력한 예제 코드입니다.

/**********************************************************************
* ex_wait3.c                                                          *
* exmple source – wait3                                               *
**********************************************************************/

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


void ViewRUsage(pid_t pid, struct rusage *pru);
int main()
{
    pid_t cpid = fork();

    if(cpid == -1)
    {
        perror("error fork");
        return 0;
    }

    if(cpid>0)
    {
        printf("<parent> fork child pid:%u\n",cpid);
        int rvalue = 0;
        struct rusage ru;
        pid_t wpid = wait3(&rvalue,0, &ru);
        ViewRUsage(wpid, &ru);
    }
    else
    {
        printf("<child> pid:%d \n",getpid());
        int i=0,j=0;
        for(i=0;i<100000;i++)
        {
            fprintf(stderr,".");
        }
    }
    return 0;
}
void ViewRUsage(pid_t pid, struct rusage *pru)
{
    printf("\n=== pid rusage info ===\n");
    struct timeval *tv = &(pru->ru_utime);
    printf("user CPU time used [%ld]sec [%ld]usec\n", tv->tv_sec,tv->tv_usec );
    tv = &(pru->ru_stime);
    printf("system CPU time used [%ld]sec [%ld]usec\n", tv->tv_sec,tv->tv_usec );
}
[그림 7.13] ex_wait3 실행 화면
[그림 7.13] ex_wait3 실행 화면