본문 바로가기
컴퓨터공학기초 개념/시스템 프로그래밍

24. 프로세스 - 프로세스 생성과 종료 총정리

by devraphy 2021. 9. 17.

0. 시작하기전에 

- 이번 포스팅에서는 이전까지 배운 프로세스 관련 시스템 콜에 대해서 총정리를 할 예정이다. 

- 추가적으로 wait() 시스템 콜에 대해서 깊이있게 다뤄볼 것이다. 


1. 프로세스 생성부터 종료까지 

- 아래의 그림을 통해서 전체적인 프로세스 생성 및 종료 과정을 복습해보자. 

 

a) 새로운 프로세스를 생성하는 방법

- fork() 시스템 콜을 이용하여 기존의 프로세스를 복사하는 방식으로 새로운 프로세스를 생성한다.

- 복사의 대상이 되는 기존 프로세스를 부모 프로세스라고 부른다.

- 복사를 통해 새로 생성된 프로세스를 자식 프로세스라고 부른다.

- 이를 통해, 프로세스 간의 계층이 형성된다. 

 

- fork() 시스템 콜은 PID 값을 반환하는데, 이를 통해서 부모와 자식 프로세스를 구분할 수 있다. 

- 자식 프로세스의 경우 ,exec() 시스템 콜을 사용하여 데이터를 덮어씌워 새로운 작업을 수행하게 할 수 있다. 

- PID를 조건문으로 사용하여, 부모 프로세스와 자식 프로세스 각각 수행하는 코드를 다르게 작성할 수 있다. 

 

 

b) 프로세스에서 wait() 시스템 콜을 사용하는 방식

- 부모 프로세스를 통해서 자식 프로세스를 생성하면, 자식 프로세스를 우선적으로 실행한다. 

- 왜냐면 부모 프로세스의 확인하에 자식 프로세스를 완전히 종료할 수 있기 때문이다. 

- 여기서 완전히 종료한다는 의미는, 부모 프로세스에 의해 자식 프로세스의 작업이 메모리에서 내려간다는 것이다. 

 

- 자식 프로세스를 우선적으로 실행하기 위해서, 자식 프로세스가 exec() 시스템 콜을 호출하면

- 부모 프로세스는 wait()라는 시스템 콜을 호출하여, 자식 프로세스가 exit() 시스템 콜을 호출할 때까지 대기한다.

- 자식 프로세스가 exit() 시스템 콜을 호출하면, 부모 프로세스에게 자식 프로세스의 작업종료를 알리는 시그널이 전달된다.

- 이 시그널을 SIGCHLD(Signal Child)이며, 부모 프로세스는 대기상태에서 재시작 상태가 된다. 

 

- 만약 자식 프로세스보다 부모 프로세스가 먼저 종료된다면, 자식 프로세스의 작업이 완료되더라도

- 메모리상에 계속해서 남아있는 좀비 프로세스 또는 고아 프로세스 상태가 되어 메모리를 낭비하게 된다. 

 

 


2. wait() 시스템 콜에 대하여

- wait() 시스템 콜에 대해서 상세하게 알아보자.

 

a) wait() 시스템 콜의 원형

#include <sys/wait.h>
pid_t wait(int *status) // 종료된 자식 프로세스의 PID가 반환된다.

- wait() 시스템콜의 특이점은 포인터 변수를 인자(parameter)로 사용된다는 것이다. 

- 왜냐면 자식 프로세스의 exit() 함수가 호출됨에 따라 부모 프로세스에서 wait()함수가 해제되기 때문이다.

- 자식 프로세스가 exit() 시스템 콜을 함수할 때, 인자로 int status가 들어간다. 

- 이 status 변수는 자식 프로세스의 종료 상태값을 의미하는데, 이를 운영체제가 wait() 시스템 콜의 인자로 넣어준다. 

- 운영체제가 status 변수가 들어가 있는 메모리의 주소를 참조하기 때문에 wait()에서는 status의 포인터 변수를 사용하는 것이다. 

- 자식 프로세스가 정상적으로 종료되어 wait() 프로세스가 실행되면 종료된 자식 프로세스의 PID를 반환한다. 

 

 

- 운영체제가 자식 프로세스가 종료됨을 확인하기 위해서 사용하는 여러가지 매크로(미리 등록된) 함수가 있다. 

- 아래의 함수는 여러가지 매크로 함수 중 하나인데, 이런 매크로 함수를 사용하여 자식 프로세스의 상태를 확인한다. 

int WIFEXITED(status); // 자식 프로세스가 정상종료되면 0이 아닌 값을 반환한다.

 

 

b) 실습예제

#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
        int child_pid;
        int status;
        int ret;
        pid = fork();
        switch(pid) {
                case -1: // fork() 함수 실패
                        perror("fork is failed\n");
                        break;
                case 0: // 자식프로세스
                        execl("/bin/ls", "ls", "-al", NULL);
                        perror("execl is faile\n");
                        break;
                default: // 부모프로세스
                        child_pid = wait(&status); // 포인터 변수를 인자로 사용, 종료된 자식 프로세스의 PID를 반환
                        printf("Parent PID (%d), Child PID (%d)\n", getpid(), child_pid);
                        ret = WIFEXITED(status); // 자식 프로세스가 정상종료되면 0이 아닌 수를 반환
                        if (ret != 0) {
                                printf("Child process is normally terminated\n");
                        } else {
                                printf("Child process is abnormally terminated\n");
                        }
                        exit(0); // 일반적으로는 return 0를 입력한다.
        }
}

 

 

▶ 실행결과

- 결과를 통해 알 수 있듯이, 자식 프로세스가 우선적으로 실행되어 execl() 함수에 의해 덮어씌어진 새로운 작업이 우선 수행된다.

- 이후 부모 프로세스의 작업이 실행됨을 알 수 있다. 

- 자식 프로세스가 온전히 종료되어, child_pid에 자식 프로세스의 PID가 잘 반환된 것을 확인할 수 있다. 

댓글