본문 바로가기
Operating Systems

차근차근 시작하는 운영체제 - Part 2: 프로세스 API

by Jimmy_iOS 2024. 12. 31.

안녕하세요! 이번에는 운영체제에서 프로세스 생성과 관리를 다루는 프로세스 API에 대해 알아보겠습니다.

운영체제의 프로세스 API는 프로세스 생성, 실행, 종료와 같은 작업을 효율적으로 관리할 수 있는 강력한 도구입니다.

이 글에서는 UNIX 운영체제의 핵심 시스템 호출인 fork(), exec(), wait()를 중심으로, 프로세스 API의 작동 원리와 응용 사례를 살펴보겠습니다.


프로세스 API란?

운영체제는 프로세스를 생성하고 제어하기 위해 특정 API를 제공합니다. UNIX 기반 시스템에서는 fork(), exec(), wait()와 같은 시스템 호출이 이러한 역할을 수행합니다.

CRUX: 프로세스 생성 및 제어

운영체제는 어떤 인터페이스를 통해 프로세스를 생성하고 제어해야 할까요? 이러한 인터페이스는 사용 편의성과 강력한 기능을 동시에 제공하도록 설계되어야 합니다.


fork() 시스템 호출

fork()는 새로운 프로세스를 생성하는 데 사용됩니다. 새로운 프로세스는 부모 프로세스의 거의 동일한 복사본으로 생성됩니다.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    printf("hello (pid:%d)\\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\\n");
        exit(1);
    } else if (rc == 0) {
        printf("child (pid:%d)\\n", (int) getpid());
    } else {
        printf("parent of %d (pid:%d)\\n", rc, (int) getpid());
    }
    return 0;
}

출력 예시

hello (pid:12345)
parent of 12346 (pid:12345)
child (pid:12346)

특징

  • 부모와 자식 프로세스: fork() 호출 이후, 두 개의 프로세스가 실행됩니다. 부모는 자식의 PID를 반환받고, 자식은 0을 반환받습니다.
  • 비결정성: 부모와 자식 프로세스의 실행 순서는 CPU 스케줄러에 따라 달라질 수 있습니다.

wait() 시스템 호출

wait()는 부모 프로세스가 자식 프로세스의 종료를 기다리게 합니다. 이를 통해 자식 프로세스가 먼저 종료된 뒤 부모 프로세스가 실행을 이어가도록 보장합니다.

#include <sys/wait.h>
int rc_wait = wait(NULL);

출력 예시

hello (pid:12345)
child (pid:12346)
parent of 12346 (rc_wait:12346) (pid:12345)

wait()는 실행 순서를 명확히 해주므로, 자식 프로세스가 항상 먼저 출력됩니다.


exec() 시스템 호출

exec()는 현재 프로세스를 다른 프로그램으로 대체하는 데 사용됩니다. fork()와 함께 사용하면 새로운 프로그램을 실행할 수 있습니다.

#include <unistd.h>
char *myargs[3];
myargs[0] = strdup("wc");  // 실행할 프로그램
myargs[1] = strdup("file.txt"); // 인자
myargs[2] = NULL;         // 종료 표시
execvp(myargs[0], myargs);

출력 예시

hello (pid:12345)
child (pid:12346)
29 107 1030 file.txt
parent of 12346 (rc_wait:12346) (pid:12345)


실제 응용: 쉘

쉘은 fork(), exec(), wait()를 활용해 사용자가 입력한 명령을 실행합니다. 쉘의 기본적인 동작은 다음과 같습니다:

  1. 사용자의 명령을 입력받음.
  2. fork()를 호출해 새로운 프로세스를 생성.
  3. 자식 프로세스에서 exec()를 호출해 명령 실행.
  4. 부모 프로세스는 wait()를 호출해 자식이 종료될 때까지 대기.

입출력 리디렉션 예시

쉘은 fork() 이후 자식 프로세스에서 표준 출력을 파일로 리디렉션할 수도 있습니다:

#include <fcntl.h>
close(STDOUT_FILENO);
open("output.txt", O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU);
execvp(myargs[0], myargs);


정리하며

fork(), exec(), wait()는 프로세스 생성과 관리의 기본이 되는 강력한 시스템 호출입니다.

UNIX 기반 시스템의 높은 유연성과 강력한 기능은 이러한 API에 의해 실현됩니다.

 

다음 글에서는 CPU 가상화를 위한 제한적 직접 실행(Limited Direct Execution)의 개념과 작동 방식을 알아보겠습니다.


다음 글에서 만나요!

이 글은 Operating Systems: Three Easy Pieces를 참고하여 작성되었습니다.