C 언어에서 clone() 시스템 콜을 사용한 고급 프로세스 생성

C 언어에서 clone() 시스템 콜은 고급 프로세스 생성 및 관리를 가능하게 해주는 강력한 도구입니다. fork()와 달리 clone()은 생성되는 프로세스가 부모 프로세스와 어떤 자원을 공유할지 세부적으로 설정할 수 있는 유연성을 제공합니다. 이 기사에서는 clone()의 기본 개념부터 활용 방법, 네임스페이스와의 결합, 실습 예제까지 포괄적으로 다루며, 이를 통해 효율적이고 세밀한 프로세스 관리를 구현하는 방법을 배울 수 있습니다.

목차

clone() 시스템 콜의 기본 개념


clone()은 Linux 운영 체제에서 제공하는 시스템 콜로, 새로운 프로세스 또는 스레드를 생성하는 기능을 제공합니다. fork()와 유사하지만, 더 높은 수준의 제어를 제공하여 부모 프로세스와 자식 프로세스 간에 공유할 자원을 세부적으로 정의할 수 있습니다.

clone()의 역할


clone()은 다음과 같은 작업에서 중요한 역할을 합니다:

  • 새로운 프로세스 생성
  • 부모와 자식 간 메모리, 파일 디스크립터, 신호 핸들러 등의 자원 공유 여부 설정
  • 리눅스 네임스페이스를 사용한 격리된 실행 환경 생성

clone() 함수 시그니처


다음은 clone()의 함수 정의입니다:

#include <sched.h>
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...);
  • fn: 새로 생성된 프로세스에서 실행할 함수의 포인터
  • child_stack: 새 프로세스의 스택 영역
  • flags: 자원 공유 및 프로세스 속성을 정의하는 플래그
  • arg: fn 함수에 전달할 인자

clone()의 특징

  • 부모와 자식이 공유할 자원을 flags 인자를 통해 세밀히 제어 가능
  • 리눅스 커널에서 스레드와 프로세스 생성 모두에 사용
  • 높은 유연성으로 컨테이너 기술 및 네임스페이스 구현에 사용

clone()은 고급 프로세스 관리와 격리된 환경 구성을 필요로 하는 시스템 개발에 있어 중요한 도구입니다.

clone()과 fork()의 차이점

fork()와 clone()의 개요

  • fork(): 부모 프로세스의 메모리와 자원을 복제하여 새로운 프로세스를 생성합니다. 자식 프로세스는 독립적으로 동작하며, 부모와 자원을 공유하지 않습니다.
  • clone(): 더 높은 수준의 제어를 제공하며, 부모와 자식 간에 공유할 자원을 선택적으로 설정할 수 있습니다. 이를 통해 메모리, 파일 디스크립터, 네임스페이스 등의 공유 여부를 세밀히 제어할 수 있습니다.

주요 차이점

특징fork()clone()
자원 공유독립적인 메모리 공간 할당플래그로 공유 여부 설정 가능
동작 방식부모 프로세스를 복제특정 자원만 공유하며 새로운 프로세스 생성
유연성제한적프로세스 및 스레드 생성 모두 가능
사용 용도일반적인 프로세스 생성고급 자원 관리 및 네임스페이스와의 결합
성능상대적으로 느림필요 자원만 공유하므로 더 빠른 동작 가능

실제 적용 사례

  • fork(): 전통적인 유닉스 환경에서 프로세스 생성에 주로 사용됩니다. 예를 들어, 데몬 프로세스 생성이나 프로세스 간 독립성이 중요한 경우에 사용됩니다.
  • clone(): 컨테이너 기술(Docker, LXC), 가상화 환경, 스레드 기반 애플리케이션에서 사용됩니다. 프로세스와 스레드 간의 자원 공유를 유연하게 설정할 수 있어 효율적인 시스템 개발이 가능합니다.

코드 예제


fork()clone()의 기본 사용법 비교:

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

#define STACK_SIZE 1024*1024  // clone()에서 사용할 스택 크기

int child_function(void *arg) {
    printf("Child process: %s\n", (char *)arg);
    return 0;
}

int main() {
    // fork() 예제
    pid_t pid = fork();
    if (pid == 0) {
        printf("Child process created using fork().\n");
    } else {
        printf("Parent process continues after fork().\n");
    }

    // clone() 예제
    char *stack = malloc(STACK_SIZE);
    if (!stack) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    char *stack_top = stack + STACK_SIZE;

    pid_t clone_pid = clone(child_function, stack_top, CLONE_VM | CLONE_FS | CLONE_FILES, "Hello from clone!");
    if (clone_pid == -1) {
        perror("clone");
        exit(EXIT_FAILURE);
    }
    printf("Parent process continues after clone().\n");

    free(stack);
    return 0;
}

요약


fork()는 단순히 새로운 프로세스를 생성하는 데 사용되는 반면, clone()은 프로세스 생성과 자원 공유를 세밀하게 제어할 수 있는 유연성을 제공합니다. 시스템 설계 요구 사항에 따라 적절한 방법을 선택하는 것이 중요합니다.

clone() 호출 시 필요한 플래그

clone() 시스템 콜에서 가장 중요한 요소 중 하나는 flags 인자입니다. 이 인자를 통해 부모 프로세스와 자식 프로세스 간에 공유할 자원을 정의할 수 있습니다. 플래그의 조합에 따라 프로세스나 스레드를 생성할 수 있으며, 다양한 환경에 맞춰 활용됩니다.

주요 플래그 설명

플래그설명
CLONE_VM부모와 자식 프로세스가 같은 메모리 공간을 공유합니다.
CLONE_FS파일 시스템 정보(현재 작업 디렉토리 등)를 부모와 자식이 공유합니다.
CLONE_FILES파일 디스크립터 테이블을 공유합니다.
CLONE_SIGHAND신호 핸들러를 공유합니다.
CLONE_THREAD부모 프로세스와 같은 스레드 그룹에 포함되도록 설정합니다.
CLONE_NEWNS새로운 마운트 네임스페이스를 생성합니다.
CLONE_NEWPID새로운 PID 네임스페이스를 생성합니다.
CLONE_NEWNET새로운 네트워크 네임스페이스를 생성합니다.

플래그 조합 예시

  1. 스레드 생성
    부모와 자식이 메모리, 파일 디스크립터, 신호 핸들러를 공유해야 하는 경우:
   CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD
  1. 네임스페이스 활용
    새로운 PID 및 네트워크 네임스페이스를 생성하여 격리된 환경을 구성:
   CLONE_NEWPID | CLONE_NEWNET

플래그 선택 시 고려 사항

  • 메모리 공유 여부: CLONE_VM을 설정하면 메모리 공간을 공유하지만, 동기화 문제를 주의해야 합니다.
  • 파일 디스크립터 관리: CLONE_FILES를 사용할 경우, 파일 디스크립터의 상태가 두 프로세스에서 동일하게 반영됩니다.
  • 네임스페이스 격리: 컨테이너 기술에서 네임스페이스 관련 플래그(CLONE_NEW*)를 설정하여 프로세스를 격리된 환경에서 실행할 수 있습니다.

예제 코드

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

#define STACK_SIZE 1024*1024  // 1MB 스택 크기

int child_function(void *arg) {
    printf("Child process: PID = %d\n", getpid());
    return 0;
}

int main() {
    char *stack = malloc(STACK_SIZE);
    if (!stack) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    char *stack_top = stack + STACK_SIZE;

    // CLONE_VM과 CLONE_FS 플래그를 사용하여 자원 공유
    pid_t pid = clone(child_function, stack_top, CLONE_VM | CLONE_FS, NULL);
    if (pid == -1) {
        perror("clone");
        free(stack);
        exit(EXIT_FAILURE);
    }

    printf("Parent process: PID = %d\n", getpid());
    free(stack);
    return 0;
}

요약


clone() 호출 시 적절한 플래그를 설정하는 것은 부모와 자식 프로세스 간의 자원 관리 방식을 결정합니다. 플래그 선택은 애플리케이션의 요구 사항과 목적에 따라 신중히 결정해야 합니다.

clone() 사용을 위한 스택 할당

clone() 시스템 콜을 사용하려면 새로 생성된 프로세스가 사용할 스택을 명시적으로 할당해야 합니다. 이는 clone()이 기존의 fork()와는 달리 자식 프로세스에 대한 스택 공간을 자동으로 생성하지 않기 때문입니다.

스택 할당의 중요성

  • 프로세스 안정성: 각 프로세스는 독립된 스택을 사용해야 데이터 손상과 충돌을 방지할 수 있습니다.
  • 성능 최적화: 적절한 크기의 스택을 할당하여 메모리를 효율적으로 사용합니다.

스택 할당 방법

  1. malloc을 사용한 동적 할당
    동적으로 스택 메모리를 할당하여 유연하게 사용합니다.
   #define STACK_SIZE 1024*1024  // 1MB 스택 크기
   char *stack = malloc(STACK_SIZE);
   if (!stack) {
       perror("malloc");
       exit(EXIT_FAILURE);
   }
   char *stack_top = stack + STACK_SIZE;  // 스택의 최상단 포인터 설정
  1. mmap을 사용한 메모리 매핑
    mmap()을 사용하여 메모리 맵핑 방식으로 스택을 할당합니다. 이는 메모리 보호 플래그를 설정할 수 있어 안전성을 높이는 데 유리합니다.
   #include <sys/mman.h>
   #define STACK_SIZE 1024*1024
   char *stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   if (stack == MAP_FAILED) {
       perror("mmap");
       exit(EXIT_FAILURE);
   }
   char *stack_top = stack + STACK_SIZE;
  1. 고정 크기의 배열 사용
    고정된 크기의 스택이 충분한 경우, 배열을 사용할 수 있습니다. 그러나 크기가 제한적이므로 작은 작업에서만 적합합니다.
   #define STACK_SIZE 1024*1024
   char stack[STACK_SIZE];
   char *stack_top = stack + STACK_SIZE;

스택 사용 시 주의점

  • 스택 크기가 너무 작으면 스택 오버플로우가 발생할 수 있으므로 작업의 복잡성을 고려하여 크기를 설정해야 합니다.
  • 동적 메모리 할당이나 mmap()을 사용했다면, 프로세스 종료 후 스택 메모리를 반드시 해제해야 합니다.

예제 코드: clone()과 스택 할당

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

#define STACK_SIZE 1024*1024  // 1MB 스택 크기

int child_function(void *arg) {
    printf("Child process: %s, PID = %d\n", (char *)arg, getpid());
    return 0;
}

int main() {
    char *stack = malloc(STACK_SIZE);
    if (!stack) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    char *stack_top = stack + STACK_SIZE;  // 스택 최상단 설정

    pid_t pid = clone(child_function, stack_top, SIGCHLD, "Hello, clone!");
    if (pid == -1) {
        perror("clone");
        free(stack);
        exit(EXIT_FAILURE);
    }

    printf("Parent process: PID = %d\n", getpid());
    free(stack);  // 스택 메모리 해제
    return 0;
}

요약


clone() 호출 시 스택은 반드시 명시적으로 할당되어야 하며, 작업에 적합한 할당 방법을 선택하는 것이 중요합니다. 적절한 크기의 스택을 설정하고 안전하게 해제하는 것을 통해 메모리 효율성과 안정성을 모두 확보할 수 있습니다.

clone()을 이용한 다중 프로세스 관리

clone() 시스템 콜은 다중 프로세스를 생성하고 관리하는 데 강력한 도구입니다. 이를 활용하면 프로세스 간 자원 공유를 세밀히 제어하며 효율적으로 작업을 수행할 수 있습니다.

다중 프로세스 관리의 필요성

  • 병렬 처리: 작업을 여러 프로세스로 분할하여 병렬로 처리하여 성능을 향상시킬 수 있습니다.
  • 자원 격리: 프로세스 간 자원 공유와 독립성을 설정하여 안전성을 보장합니다.
  • 유연성: 특정 작업을 분리된 환경에서 수행할 수 있도록 지원합니다.

다중 프로세스 생성 흐름

  1. 스택 할당
    각 프로세스가 독립적인 스택을 가져야 하므로 각 프로세스를 위한 스택을 할당합니다.
  2. clone() 호출
    자원 공유 플래그를 설정하여 필요한 프로세스를 생성합니다.
  3. 프로세스 관리
    생성된 프로세스의 상태를 추적하고 종료를 처리합니다.

다중 프로세스 관리 코드 예제

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

#define STACK_SIZE 1024*1024  // 각 프로세스에 할당할 스택 크기
#define NUM_PROCESSES 3       // 생성할 프로세스 수

int child_function(void *arg) {
    printf("Child process %d started with PID = %d\n", *(int *)arg, getpid());
    sleep(2);  // 프로세스 작업 시뮬레이션
    printf("Child process %d finished\n", *(int *)arg);
    return 0;
}

int main() {
    char *stacks[NUM_PROCESSES];
    pid_t pids[NUM_PROCESSES];
    int process_ids[NUM_PROCESSES];

    for (int i = 0; i < NUM_PROCESSES; i++) {
        // 스택 메모리 동적 할당
        stacks[i] = malloc(STACK_SIZE);
        if (!stacks[i]) {
            perror("malloc");
            exit(EXIT_FAILURE);
        }
        char *stack_top = stacks[i] + STACK_SIZE;

        // 프로세스 ID 설정
        process_ids[i] = i + 1;

        // clone() 호출
        pids[i] = clone(child_function, stack_top, SIGCHLD, &process_ids[i]);
        if (pids[i] == -1) {
            perror("clone");
            exit(EXIT_FAILURE);
        }
        printf("Parent: Created process %d with PID = %d\n", i + 1, pids[i]);
    }

    // 모든 자식 프로세스 종료 대기
    for (int i = 0; i < NUM_PROCESSES; i++) {
        waitpid(pids[i], NULL, 0);
        free(stacks[i]);  // 할당된 스택 메모리 해제
        printf("Parent: Process %d with PID = %d has exited\n", i + 1, pids[i]);
    }

    printf("Parent process finished\n");
    return 0;
}

코드 설명

  1. 스택 할당: 각 프로세스를 위해 1MB 크기의 스택을 할당합니다.
  2. 프로세스 생성: clone()을 호출하여 NUM_PROCESSES 개의 프로세스를 생성합니다.
  3. 프로세스 종료 대기: waitpid()를 사용해 자식 프로세스가 종료될 때까지 기다립니다.
  4. 자원 해제: 스택 메모리를 해제하여 메모리 누수를 방지합니다.

프로세스 관리 시 유의점

  • 자원 공유 관리: 플래그를 설정하여 부모와 자식 간 적절한 자원 공유를 설정해야 합니다.
  • 스택 메모리 관리: 프로세스 종료 후 스택 메모리를 반드시 해제해야 합니다.
  • 프로세스 동기화: 프로세스 간의 동기화 문제가 발생하지 않도록 상태를 관리해야 합니다.

요약


clone()을 활용하면 다중 프로세스를 유연하게 생성하고 관리할 수 있습니다. 자원 공유와 스택 관리에 유의하며 병렬 처리를 통해 효율적인 애플리케이션을 개발할 수 있습니다.

clone()과 네임스페이스 관리

Linux 네임스페이스는 프로세스 간 자원 격리를 지원하는 핵심 기술이며, clone() 시스템 콜을 사용하여 새로운 네임스페이스를 생성할 수 있습니다. 이를 통해 컨테이너화된 환경이나 가상화된 실행 환경을 구성할 수 있습니다.

네임스페이스란 무엇인가


네임스페이스는 Linux 커널에서 제공하는 기능으로, 프로세스가 특정 자원을 독립적으로 볼 수 있도록 격리된 실행 환경을 제공합니다. 이를 통해 동일한 시스템 내에서도 독립적인 파일 시스템, 네트워크, PID 등을 사용할 수 있습니다.

네임스페이스 유형

유형설명
UTS호스트 이름과 도메인 이름 격리
PID프로세스 ID 번호 격리
NET네트워크 장치, 포트, IP 주소 격리
MOUNT파일 시스템 마운트 격리
USER사용자 ID 및 그룹 ID 격리
IPC프로세스 간 통신(IPC) 자원 격리

clone()과 네임스페이스


clone()은 네임스페이스 생성 시 핵심적으로 사용됩니다. flags 인자에 네임스페이스 관련 플래그(CLONE_NEW*)를 설정하면 새 프로세스를 격리된 환경에서 실행할 수 있습니다.

네임스페이스 생성 코드 예제

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define STACK_SIZE 1024*1024  // 스택 크기

int child_function(void *arg) {
    printf("Child process: Inside new namespace\n");
    printf("Hostname before change: ");
    system("hostname");

    // 네임스페이스 내에서 호스트 이름 변경
    if (sethostname("NewNamespace", 12) == -1) {
        perror("sethostname");
        return -1;
    }
    printf("Hostname after change: ");
    system("hostname");
    return 0;
}

int main() {
    char *stack = malloc(STACK_SIZE);
    if (!stack) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    char *stack_top = stack + STACK_SIZE;

    // CLONE_NEWUTS 플래그로 UTS 네임스페이스 생성
    pid_t pid = clone(child_function, stack_top, CLONE_NEWUTS | SIGCHLD, NULL);
    if (pid == -1) {
        perror("clone");
        free(stack);
        exit(EXIT_FAILURE);
    }

    // 부모 프로세스 대기
    if (waitpid(pid, NULL, 0) == -1) {
        perror("waitpid");
    }

    free(stack);  // 스택 메모리 해제
    printf("Parent process: Exiting\n");
    return 0;
}

코드 설명

  1. UTS 네임스페이스 생성: CLONE_NEWUTS 플래그를 사용해 격리된 호스트 이름과 도메인 이름 환경을 생성합니다.
  2. 호스트 이름 변경: 자식 프로세스는 독립된 네임스페이스 내에서 새로운 호스트 이름을 설정합니다.
  3. 부모와 자식의 독립성 확인: 부모 프로세스는 기존 환경에 영향을 받지 않고 정상적으로 작동합니다.

네임스페이스 활용 사례

  • 컨테이너 기술: Docker, Kubernetes 등은 네임스페이스를 활용해 자원을 격리합니다.
  • 테스트 환경: 애플리케이션 테스트 시 독립적인 실행 환경 제공
  • 보안 강화: 프로세스와 자원을 분리해 악성 코드로부터 시스템을 보호

주의 사항

  • 네임스페이스를 생성하려면 루트 권한이 필요합니다.
  • 잘못된 네임스페이스 설정은 프로세스 동작에 영향을 줄 수 있으므로 플래그 사용에 주의해야 합니다.

요약


clone()과 네임스페이스를 결합하면 프로세스와 자원을 격리하여 독립적인 환경을 구성할 수 있습니다. 이는 컨테이너화된 애플리케이션과 가상화된 환경 구축의 핵심 기술로, 시스템 설계와 보안 향상에 중요한 역할을 합니다.

clone() 사용 시 발생할 수 있는 오류와 해결책

clone() 시스템 콜은 유연성과 강력한 기능을 제공하지만, 사용 시 다양한 오류 상황에 직면할 수 있습니다. 이러한 오류는 잘못된 플래그 설정, 스택 관리 문제, 권한 부족 등으로 인해 발생합니다.

주요 오류와 원인

오류 유형원인
EINVAL잘못된 플래그 조합 또는 child_stack이 유효하지 않은 경우
ENOMEM스택 메모리 부족 또는 프로세스 생성 제한 초과
EPERM특정 네임스페이스 플래그 사용 시 루트 권한 부족
EFAULT잘못된 포인터 전달로 인해 메모리에 접근할 수 없는 경우

오류 해결책

  1. 플래그 검증
  • 올바른 플래그 조합을 사용했는지 확인합니다.
  • 서로 호환되지 않는 플래그를 함께 사용하지 않도록 주의합니다.
   if (flags & CLONE_NEWUSER && !(flags & CLONE_NEWPID)) {
       fprintf(stderr, "CLONE_NEWUSER requires CLONE_NEWPID.\n");
       return -1;
   }
  1. 스택 메모리 확인
  • malloc 또는 mmap으로 충분한 크기의 스택을 할당합니다.
  • stack_top이 유효한 메모리 주소를 가리키는지 확인합니다.
   if (!child_stack) {
       fprintf(stderr, "Invalid stack pointer.\n");
       return -1;
   }
  1. 권한 문제 해결
  • 네임스페이스 관련 플래그(CLONE_NEWUSER, CLONE_NEWPID 등)는 루트 권한이 필요합니다.
  • 권한 부족 시, 실행 명령어에 sudo를 사용하거나 적절한 권한을 부여합니다.
  1. 메모리 초과 방지
  • 시스템의 프로세스 생성 한도를 확인하고, 필요한 경우 /etc/security/limits.conf 파일을 수정합니다.
   sudo nano /etc/security/limits.conf
   # 사용자별 프로세스 한도 증가
   username hard nproc 1024
  1. 디버깅 툴 활용
  • strace를 사용해 clone() 호출 시 발생하는 시스템 콜 동작을 추적합니다.
   strace -e trace=clone ./program

예제: 주요 오류를 유발하는 코드와 수정된 코드

오류 코드:

#define STACK_SIZE 1024
char *stack;  // 스택이 초기화되지 않음
pid_t pid = clone(child_function, stack, CLONE_VM | SIGCHLD, NULL);

수정된 코드:

#define STACK_SIZE 1024*1024
char *stack = malloc(STACK_SIZE);
if (!stack) {
    perror("malloc");
    exit(EXIT_FAILURE);
}
char *stack_top = stack + STACK_SIZE;  // 스택 최상단 설정
pid_t pid = clone(child_function, stack_top, CLONE_VM | SIGCHLD, NULL);

실제 오류 디버깅 예시

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

#define STACK_SIZE 1024*1024

int child_function(void *arg) {
    printf("Child process running\n");
    return 0;
}

int main() {
    char *stack = malloc(STACK_SIZE);
    if (!stack) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    char *stack_top = stack + STACK_SIZE;

    pid_t pid = clone(child_function, stack_top, CLONE_VM | SIGCHLD, NULL);
    if (pid == -1) {
        perror("clone");
        free(stack);
        exit(EXIT_FAILURE);
    }

    if (waitpid(pid, NULL, 0) == -1) {
        perror("waitpid");
    }

    free(stack);
    return 0;
}

출력 분석:

  • 오류 메시지가 발생하면 errno 값을 확인해 문제를 파악합니다.
  • EINVAL, ENOMEM, EPERM에 따라 위에서 제시한 해결책을 적용합니다.

요약


clone() 사용 시 발생할 수 있는 오류는 대부분 잘못된 플래그 설정, 메모리 관리 부족, 권한 부족 등으로 인해 발생합니다. 사전에 플래그와 스택을 검증하고 디버깅 도구를 활용하면 문제를 효과적으로 해결할 수 있습니다.

실습: clone() 기반 간단한 프로그램 작성

clone() 시스템 콜을 실습하며, 자식 프로세스를 생성하고 부모와 자식 간의 동작을 이해하는 간단한 프로그램을 작성합니다.

실습 목표

  • clone()을 사용해 자식 프로세스를 생성하고 실행
  • 부모와 자식의 독립적인 작업 수행
  • 프로세스 종료 후 자원 해제

프로그램 코드

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define STACK_SIZE 1024*1024  // 1MB 스택 크기

int child_function(void *arg) {
    printf("Child process: Hello from clone! PID = %d\n", getpid());
    for (int i = 0; i < 5; i++) {
        printf("Child process working: %d\n", i + 1);
        sleep(1);  // 작업 시뮬레이션
    }
    printf("Child process: Exiting\n");
    return 0;
}

int main() {
    char *stack = malloc(STACK_SIZE);
    if (!stack) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    char *stack_top = stack + STACK_SIZE;

    printf("Parent process: Creating child process\n");

    pid_t pid = clone(child_function, stack_top, SIGCHLD, NULL);
    if (pid == -1) {
        perror("clone");
        free(stack);
        exit(EXIT_FAILURE);
    }

    printf("Parent process: Waiting for child process to finish\n");

    if (waitpid(pid, NULL, 0) == -1) {
        perror("waitpid");
        free(stack);
        exit(EXIT_FAILURE);
    }

    printf("Parent process: Child process has exited\n");

    free(stack);  // 스택 메모리 해제
    return 0;
}

코드 설명

  1. 스택 할당
  • malloc()을 사용하여 1MB 크기의 스택을 할당합니다.
  • stack_top 포인터는 스택의 최상단을 가리킵니다.
  1. clone() 호출
  • clone()을 호출하여 자식 프로세스를 생성합니다.
  • SIGCHLD 플래그를 사용해 자식 프로세스가 종료되면 부모에게 시그널을 보냅니다.
  1. 자식 프로세스 작업
  • 자식 프로세스는 반복 작업(1초 간격의 출력)을 수행합니다.
  1. 부모 프로세스 대기
  • waitpid()를 호출하여 자식 프로세스가 종료될 때까지 기다립니다.
  1. 자원 해제
  • 스택 메모리를 해제하여 메모리 누수를 방지합니다.

출력 결과 예시

Parent process: Creating child process
Parent process: Waiting for child process to finish
Child process: Hello from clone! PID = 12345
Child process working: 1
Child process working: 2
Child process working: 3
Child process working: 4
Child process working: 5
Child process: Exiting
Parent process: Child process has exited

실습 체크리스트

  • 프로세스 생성 확인: 자식 프로세스가 정상적으로 생성되고 작업을 수행하는지 확인합니다.
  • 부모-자식 관계: 자식 프로세스 종료 후 부모 프로세스가 올바르게 종료를 인식하는지 확인합니다.
  • 자원 관리: 스택 메모리가 해제되었는지 확인하여 메모리 누수를 방지합니다.

확장 아이디어

  • 자식 프로세스에서 부모 프로세스와 데이터를 공유하거나 IPC(프로세스 간 통신)를 구현해 보세요.
  • 네임스페이스 플래그(CLONE_NEWUTS, CLONE_NEWPID)를 추가해 격리된 환경을 구성하세요.

요약


본 실습을 통해 clone() 시스템 콜의 기본적인 사용법과 부모-자식 프로세스 간의 동작 원리를 이해할 수 있습니다. 이는 더 복잡한 프로세스 관리와 응용 프로그램 개발의 기초가 됩니다.

요약


이 기사에서는 clone() 시스템 콜의 기본 개념과 활용 방법을 다루었습니다. clone()을 통해 부모와 자식 프로세스 간 자원 공유를 세밀히 설정하고, 네임스페이스를 활용하여 격리된 실행 환경을 구성할 수 있습니다. 스택 관리, 플래그 설정, 다중 프로세스 관리 및 주요 오류 해결 방법을 실습과 함께 설명하였습니다. 이를 통해 고급 프로세스 생성과 관리 기술을 효과적으로 익힐 수 있습니다.

목차