정보처리기사/프로그래밍 언어 활용

Fork() 정리

glorypang 2025. 10. 24. 22:20
728x90
반응형
SMALL

1. Fork란?

Fork는 Unix/Linux 시스템에서 새로운 프로세스를 생성하는 시스템 콜입니다.
현재 실행 중인 프로세스(부모 프로세스)를 복제하여 새로운 프로세스(자식 프로세스)를 만듭니다.

2. Fork의 특징

  • 프로세스 복제: 부모 프로세스의 메모리, 파일 디스크립터, 레지스터 등을 복사
  • 독립적 실행: 자식 프로세스는 부모와 독립적으로 실행
  • 반환값 차이:
    • 부모 프로세스: 자식의 PID(Process ID) 반환
    • 자식 프로세스: 0 반환
    • 실패 시: -1 반환

3. 기본 예제

예제 1: 간단한 Fork

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

int main() {
    pid_t pid;
    
    printf("Fork 실행 전 - PID: %d\n", getpid());
    
    pid = fork();
    
    if (pid < 0) {
        // Fork 실패
        fprintf(stderr, "Fork 실패\n");
        return 1;
    }
    else if (pid == 0) {
        // 자식 프로세스
        printf("자식 프로세스 - PID: %d, 부모 PID: %d\n", 
               getpid(), getppid());
    }
    else {
        // 부모 프로세스
        printf("부모 프로세스 - PID: %d, 자식 PID: %d\n", 
               getpid(), pid);
    }
    
    printf("프로세스 %d 종료\n", getpid());
    return 0;
}

출력 예시:

Fork 실행 전 - PID: 1234
부모 프로세스 - PID: 1234, 자식 PID: 1235
프로세스 1234 종료
자식 프로세스 - PID: 1235, 부모 PID: 1234
프로세스 1235 종료
출력 순서는 실행할 때마다 바뀔 수 있습니다.

예제 2: 부모-자식 프로세스의 변수 독립성

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

int main() {
    int x = 10;
    pid_t pid = fork();
    
    if (pid == 0) {
        // 자식 프로세스
        x = 20;
        printf("자식: x = %d\n", x);
    }
    else {
        // 부모 프로세스
        wait(NULL);  // 자식이 끝날 때까지 대기
        printf("부모: x = %d\n", x);
    }
    
    return 0;
}

출력:

자식: x = 20
부모: x = 10

예제 3: 다중 Fork

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

int main() {
    printf("시작\n");
    
    fork();  // 첫 번째 fork - 2개 프로세스
    fork();  // 두 번째 fork - 4개 프로세스
    
    printf("PID: %d\n", getpid());
    
    return 0;
}

출력 (총 4개 프로세스):

시작
PID: 1234
PID: 1235
PID: 1236
PID: 1237

예제 4: exec()와 함께 사용

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

int main() {
    pid_t pid = fork();
    
    if (pid == 0) {
        // 자식 프로세스: 다른 프로그램 실행
        printf("자식: ls 명령어 실행\n");
        execlp("/bin/ls", "ls", "-l", NULL);
        
        // exec이 성공하면 이 코드는 실행되지 않음
        perror("exec 실패");
    }
    else {
        // 부모 프로세스
        printf("부모: 자식 프로세스 대기 중...\n");
        wait(NULL);
        printf("부모: 자식 프로세스 종료됨\n");
    }
    
    return 0;
}

출력

부모: 자식 프로세스 대기 중...
자식: ls 명령어 실행
total 24
-rwxr-xr-x 1 user user 16784 Oct 24 10:30 a.out
-rw-r--r-- 1 user user   445 Oct 24 10:29 fork_exec.c
-rw-r--r-- 1 user user   123 Oct 24 09:15 test.txt
부모: 자식 프로세스 종료됨

또는 (자식이 먼저 실행되면):

자식: ls 명령어 실행
total 24
-rwxr-xr-x 1 user user 16784 Oct 24 10:30 a.out
-rw-r--r-- 1 user user   445 Oct 24 10:29 fork_exec.c
-rw-r--r-- 1 user user   123 Oct 24 09:15 test.txt
부모: 자식 프로세스 대기 중...
부모: 자식 프로세스 종료됨

예제 5: 여러 자식 프로세스 생성

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

int main() {
    int n = 3;
    
    for (int i = 0; i < n; i++) {
        pid_t pid = fork();
        
        if (pid == 0) {
            // 자식 프로세스
            printf("자식 %d - PID: %d\n", i, getpid());
            sleep(1);
            return 0;  // 자식은 여기서 종료
        }
    }
    
    // 부모 프로세스만 여기 도달
    printf("부모 프로세스: 모든 자식 대기 중\n");
    
    // 모든 자식 프로세스 종료 대기
    for (int i = 0; i < n; i++) {
        wait(NULL);
    }
    
    printf("부모 프로세스: 모든 자식 종료됨\n");
    return 0;
}

가능한 출력 결과들

출력 예시 1: 순차적으로 출력
자식 0 - PID: 1235
자식 1 - PID: 1236
자식 2 - PID: 1237
부모 프로세스: 모든 자식 대기 중
부모 프로세스: 모든 자식 종료됨


출력 예시 2: 역순으로 출력
자식 2 - PID: 1237
자식 1 - PID: 1236
자식 0 - PID: 1235
부모 프로세스: 모든 자식 대기 중
부모 프로세스: 모든 자식 종료됨

출력 예시 3: 섞여서 출력
자식 0 - PID: 1235
자식 2 - PID: 1237
자식 1 - PID: 1236
부모 프로세스: 모든 자식 대기 중
부모 프로세스: 모든 자식 종료됨

출력 예시 4: 부모가 먼저 출력
부모 프로세스: 모든 자식 대기 중
자식 0 - PID: 1235
자식 1 - PID: 1236
자식 2 - PID: 1237
부모 프로세스: 모든 자식 종료됨

출력 예시 5: 완전히 섞임
자식 0 - PID: 1235
부모 프로세스: 모든 자식 대기 중
자식 1 - PID: 1236
자식 2 - PID: 1237
부모 프로세스: 모든 자식 종료됨

4. Fork의 주요 용도

  1. 병렬 처리: 여러 작업을 동시에 수행
  2. 서버 프로그래밍: 클라이언트 요청마다 새 프로세스 생성
  3. 프로그램 실행: fork() + exec()로 새 프로그램 실행
  4. 데몬 프로세스: 백그라운드 서비스 생성

5. 주의사항

  • 좀비 프로세스: 자식이 종료되었지만 부모가 wait()하지 않으면 발생
  • 고아 프로세스: 부모가 먼저 종료되면 자식은 init 프로세스에 입양됨
  • 리소스 소비: Fork는 메모리를 복사하므로 비용이 큼 (COW 기법으로 최적화)
728x90
반응형
LIST