[운영체제] 프로세스에 대한 연산

2024. 9. 4. 14:25CS/OS

대부분 시스템 내의 프로세스들은 병행 실행될 수 있으며, 반드시 동적으로 생성되고 제거되어야 한다. 그러므로 운영체제는 프로세스 생성, 종료를 위한 기법을 제공해야 한다.

 

프로세스 생성 및 종료 과정

 

실행되는 동안 프로세스는 여러 개의 새로운 프로세스들을 생성할 수 있다. 같이 생성한 프로세스는 부모 프로세스 새로운 프로세스는 자식 프로세스라 부른다. 때문에 프로세스 구조는 트리구조를 형성한다.

 

대부분의 운영체제에서는 PID (프로세스 식별자 -> DB -> 유일키 역할)를 사용해 프로세스의 고유값을 할당해 구별한다.

 

리눅스 시스템 프로세스 트리 구조

 

일반적으로 프로세스가 자식 프로세스를 생성할 때 그 자식 프로세스는 자신의 임무를 달성하기 위하여 CPU 시간, 메모리, 파일, 입출력 장치가 필요한다. 자식프로세스는 이 자원을 운영체제로부터 직접 얻거나, 부모 프로세스가 가진 자원의  프로세스에게 나누어 주거나 메모리나 파일과 같은 몇몇 자원들은 자식 프로세스들이 같이 사용하게 할 수 도 있다. 이를 통해 자식 프로세스의 메모리 자원 통제와 자식 프로세스들이 자식 프로세스들을 많이 생성해 시스템 과부하에 걸릴 수 있게 만드는 상황 또한 예방한다. (태초의 자식 프로세스 자원 내에서 자식 프로세스들을 생성할 수 있기 때문이다.)

 

자식 프로세스에게 데이터 전달

   부모 프로세스는 자식 프로세스를 생성할 때 초기화 데이터를 전달할 수 있다.

   예를 들면 hw1.c 파일이 부모 프로세스에 존재할 때 자식 프로세스는 해당 파일을 부모에게 전달 받아 입출력이 가능하다.

또는 부모가 자식에게 미리 열린 파일 값을 전달해 자식 프로세스는 열린 파일을 화면에 출력하기만 하는 역할을 수행할 수 도 있다. 한마디로 자식 프로세스의 실행 방식 설정이 가능하다는 것이다.

 

프로세스 생성 후 실행 방식  (동기/비동기 방식과 유사)

자식 프로세스를 생성할 때 부모 프로세스는 두 가지 방법 중 하나를 선택할 수 있다.

  1. 병행 실행: 자식 프로세스를 생성 후 자신의 작업을 계속 진행 함.
  2. 자식 종료 대기: 부모 프로세스가 자식을 만둔 후, 자식 프로세스가 일을 끝낼 때까지 기다린다.

이러한 생성 분리를 위해서는 UNIX 산하 운영체제에서는 fork() 명령어를 이용해 프로세스를 제어한다. 자식 프로세스는  fork() 호출 상태로 새로운 프로세스를 생성한다. fork 함수는 부모와 자식 프로세스에 모두 반환되지만, 반환되는 값은 다르다. 부모에게는 자식 프로세스 PID를 반환하고 자식 프로세스는 0을 반환한다. 이때 부모는 자식 프로세스의 종료를 기다리며 wait() 함수로 프로세스 종료 대기를 한다. 이후 자식 프로세스 PID가 반환되면 부모 프로세스가 마저 실행된다. 이렇게 종료된 경우 프로세스 마지막 문장에 exit 시스템 콜을 이용해 운영체제에게 사용한 자원들을 반환해줘야한다.

 

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

int main() {
    pid_t pid;

    // fork 호출
    pid = fork();

    if (pid < 0) {
        // fork 실패 시
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (pid == 0) {
        // 자식 프로세스
        printf("자식 프로세스 My PID is %d\n", getpid());
        // 여기서 자식 프로세스만의 작업을 수행할 수 있습니다.
    } else {
        // 부모 프로세스
        printf("부모 프로세스 My PID is %d, Child PID is %d\n", getpid(), pid);
        // 부모 프로세스는 자식 프로세스의 작업이 끝날 때까지 기다릴 수 있습니다.
        wait(NULL);  // 자식 프로세스의 종료를 기다림
        printf("자식 프로세스 완료");
    }

    return 0;
}


자식 프로세스 My PID is 960
부모 프로세스 My PID is 959, Child PID is 960
자식 프로세스 완료
[Execution complete with exit code 0]

 

상단 코드 자식 프로세스에서는 명식적으로 exit() 함수를 사용해 종료하지 않았지만 프로세스가 종료되면 자동으로 exit이 호출된다. 때문에 명시적인 호출이 필요하다면 _exit() 코드를 추가하면 명시적인 호출 결과를 확인해 볼 수 있다.

 

요약

  • 부모 프로세스는 자식 프로세스를 생성할 때 필요한 초기 데이터나 자원을 전달할 수 있다.
  • 부모와 자식은 병행해서 실행되거나 부모가 자식의 작업이 끝날 때까지 기다릴 수 있다.
  • 자식 프로세스는 부모 프로세스의 복사본이 될 수도 있고 완전히 새 프로그램이 실행될 수도 있다.
좀비 프로세스

 

좀비 프로세스는 자식 프로세스가 종료되었지만, 부모 프로세스가 wait() 또는 waitpid()를 호출하여 자식 프로세스의 종료 상태를 수집하지 않은 상태에서 남아 있는 프로세스를 의미한다. 이 상태에서는 자식 프로세스는 더 이상 실행 중이지 않지만 프로세스 테이블에 해당 자식 프로세스의 종료 상태를 담고 있는 항목이 남아 있게 된다. 이 항목은 부모 프로세스가 종료 상태를 수집할 때까지 시스템 자원을 소모하며 "좀비" 상태로 남아 있게된다.

 

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

int main() {
    pid_t pid, child_pid;
    int status;

    // 자식 프로세스 생성
    pid = fork();

    if (pid < 0) {
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (pid == 0) {
        // 자식 프로세스
        printf("자식 프로세스. My PID is %d\n", getpid());
        sleep(6);  // 자식 프로세스를 30초 동안 대기시킴
        exit(0);    // 자식 프로세스 종료
    } else {
        // 부모 프로세스
        printf("부모 프로세스. My PID is %d, Child PID is %d\n", getpid(), pid);

        // 부모 프로세스가 자식 프로세스가 종료될 때까지 대기하지 않고,
        // 자식 프로세스를 좀비 상태로 남기기 위해 10초 동안 대기
        sleep(10);

        // 자식 프로세스가 종료되었는지 확인
        child_pid = waitpid(pid, &status, WNOHANG);

        if (child_pid == 0) {
            printf("자식 프로세스가 아직 종료되지 않았습니다.\n");
        } else if (child_pid == pid) {
            printf("자식 프로세스가 종료되었습니다.\n");

            if (WIFEXITED(status)) {
                printf("자식 프로세스는 정상적으로 종료되었습니다. 종료 코드: %d\n", WEXITSTATUS(status));
            } else if (WIFSIGNALED(status)) {
                printf("자식 프로세스가 신호에 의해 종료되었습니다. 신호 번호: %d\n", WTERMSIG(status));
            } else if (WIFSTOPPED(status)) {
                printf("자식 프로세스가 중단 상태입니다. 중단된 신호 번호: %d\n", WSTOPSIG(status));
            }
        } else {
            perror("waitpid 오류");
        }

        // 자식 프로세스의 종료 상태를 수집하여 좀비 프로세스를 제거
        if (child_pid != 0) {
            while (waitpid(-1, NULL, WNOHANG) > 0) {
                // 자식 프로세스의 상태를 수집
            }
        }
    }

    return 0;
}

'CS > OS' 카테고리의 다른 글

[운영체제] 스레드와 병행성  (0) 2024.09.27
[운영체제] 프로세스 간 통신  (0) 2024.09.11
[운영체제] 프로세스란  (0) 2024.08.22