fork (UNIX)

fork는 유닉스 계열 운영체제에서 프로세스를 생성하기 위해 제공되는 시스템 콜이며, 호출된 프로세스(부모)를 복제하여 새로운 프로세스(자식)를 만든다. 이 시스템 콜은 프로세스 복제의 기본 메커니즘으로, 대부분의 유닉스 기반 시스템에서 동일한 동작을 보인다.

정의

  • fork() 함수는 현재 실행 중인 프로세스의 주소 공간, 파일 디스크립터, 환경 변수, 프로세스 상태 등을 그대로 복사한 새 프로세스를 생성한다.
  • 호출 성공 시, 부모 프로세스에게는 자식 프로세스의 PID(Process ID)가 반환되고, 자식 프로세스에게는 0이 반환된다. 오류가 발생하면 ‑1이 반환되고 errno에 오류 원인이 설정된다.

역사

  • 최초의 fork() 구현은 1975년 발표된 UNIX Version 6에 포함되었으며, 이후 BSD, System V, Linux, macOS 등 다양한 유닉스 파생 시스템에 그대로 채택되었다.
  • 초기 구현은 프로세스 복제 시 전체 메모리를 복사했지만, 현대 구현에서는 Copy‑On‑Write(COW) 기법을 사용해 실제 메모리 복사를 지연시켜 효율성을 높인다.

동작 원리

  1. 프로세스 테이블 엔트리 복제: 부모의 프로세스 테이블 엔트리를 복사하여 자식 엔트리를 만든다.
  2. 주소 공간 복사: COW 방식에 따라 페이지 테이블만 복제하고 실제 물리 메모리는 필요 시 복사한다.
  3. 파일 디스크립터 복제: 부모와 자식이 동일한 파일 디스크립터를 공유하되, 파일 오프셋은 각각 독립적으로 유지된다.
  4. 시그널 핸들러 복사: 부모가 설정한 시그널 핸들러가 자식에게 그대로 전달된다.
  5. 리턴값 차별: 반환값을 기준으로 부모와 자식이 각각 다른 코드 경로를 수행할 수 있다.

관련 시스템 콜

  • exec: fork() 후에 자식 프로세스가 새로운 프로그램 이미지를 로드하는 데 사용된다. 흔히 fork‑exec 패턴으로 프로세스 생성 후 실행 파일 교체가 이루어진다.
  • vfork: 부모 프로세스가 자식이 exec 혹은 _exit을 호출할 때까지 실행을 일시 중단하도록 설계된 변형이다. 효율성은 높지만 사용 시 주의가 필요하다.
  • clone: Linux에서 제공하는 시스템 콜로, fork()와 달리 공유 자원(메모리, 파일 디스크립터 등)을 세부적으로 지정할 수 있다. 주로 스레드 구현에 활용된다.

사용 예시 (C 언어)

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

int main(void) {
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        /* 자식 프로세스 */
        printf("Child process, PID = %d
", getpid());
        // exec, 작업 수행 등
        _exit(0);
    } else {
        /* 부모 프로세스 */
        printf("Parent process, child PID = %d
", pid);
        // wait 등으로 자식 종료 대기 가능
    }
    return 0;
}

특징 및 제한

  • 동시성: fork()는 프로세스 복제 시점에 전역 변수나 파일 디스크립터 등이 복제되므로, 자식과 부모가 독립적으로 진행될 수 있다. 그러나 복제 직후에는 두 프로세스가 동일한 메모리 페이지를 공유하므로, 쓰기 작업이 발생하면 COW가 동작한다.
  • 시그널: 복제 시점에 설정된 시그널 핸들러가 그대로 복사된다. 자식이 시그널을 수신하면 부모와 별개로 처리된다.
  • 리소스 제한: 시스템에 따라 한 사용자당 생성 가능한 프로세스 수(ulimit -u)에 제한이 있다. fork() 호출이 이 한계를 초과하면 ENOMEM 오류가 반환될 수 있다.
  • 스레드와의 호환성: 멀티스레드 프로그램에서 fork()를 호출하면 자식 프로세스는 호출 시점에 존재하던 하나의 스레드만 복제된다. 이 경우 pthread_atfork 핸들러를 이용해 재설정이 필요하다.

표준화

  • POSIX.1-2001 및 이후 표준에 fork()가 정의되어 있다. 함수 시그니처는 pid_t fork(void);이며, 오류 시 errnoEAGAIN, ENOMEM, ENOSYS 등이 설정된다.

참고 문헌

  • IEEE Std 1003.1, “POSIX.1-2008 – System Interfaces”.
  • Maurice J. Bach, The Design of the Unix Operating System, Prentice Hall, 1986.
  • Robert Love, Linux System Programming, O'Reilly Media, 2013.
둘러보기

더 찾아볼 만한 주제