티스토리 뷰

반응형

fork()

'fork()' 함수는 UNIX 계열 운영 체제에서 프로세스를 생성하는 데 사용됩니다. 'fork()'를 호출하면 현재 프로세스(부모 프로세스)가 새로운 프로세스(자식 프로세스)를 생성하며, 자식 프로세스는 부모 프로세스의 복사본이 됩니다. 이때 부모와 자식은 거의 동일한 상태로 실행되지만, 각기 다른 프로세스 ID를 가지며, 부모와 자식은 메모리 공간을 공유하지 않습니다.

 

fork() 함수가 호출되면, 현재 실행 중인 프로세스의 복제본이 생성됩니다. 부모 프로세스와 완전히 동일한 프로그램 코드와 상태를 가지는 자식 프로세스가 생성됩니다. 이 때, 자식 프로세스는 fork() 함수 호출 직후의 상태에서 실행이 시작됩니다.

자식 프로세스가 현재 프로그램의 처음부터 다시 시작되지 않음.
따라서, 자식 프로세스는 무한 복제되지 않음.

사용법

헤더 파일
#include <unistd.h>
#include <sys/types.h>

함수 프로토타입
pid_t fork(void);

반환 값
- 부모 프로세스에서는 자식 프로세스의 PID를 반환합니다.
- 자식 프로세스에서는 0을 반환합니다.
- 실패 시 -1을 반환하며, 오류 원인은 'errno'에 설정됩니다.

예제
다음은 'fork()'를 사용하여 프로세스를 생성하고, 부모와 자식 프로세스가 각각 다른 작업을 수행하는 간단한 예제입니다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
    pid_t pid;

    // 새로운 프로세스를 생성
    pid = fork();

    if (pid == -1) {
        // fork() 실패
        perror("fork");
        exit(1);
    }

    if (pid == 0) {
        // 자식 프로세스
        printf("Hello from the child process! PID: %d\n", getpid());
        printf("Child's parent PID: %d\n", getppid());
    } else {
        // 부모 프로세스
        printf("Hello from the parent process! PID: %d\n", getpid());
        printf("Parent's child PID: %d\n", pid);
    }

    return 0;
}


설명

1. 'fork()' 호출:
    pid = fork();
   - 'fork()'를 호출하여 새로운 프로세스를 생성합니다. 이 시점에서 두 프로세스가 동일한 코드를 실행합니다.

2. 오류 처리:
    if (pid == -1) {
       perror("fork");
       exit(1);
   }
   - 'fork()'가 실패하면 '-1'을 반환하고, 오류 메시지를 출력한 후 프로그램을 종료합니다.

3. 자식 프로세스 코드:
   if (pid == 0) {
       printf("Hello from the child process! PID: %d\n", getpid());
       printf("Child's parent PID: %d\n", getppid());
   }

   - 'pid'가 0이면 자식 프로세스임을 의미합니다. 자식 프로세스는 자신의 프로세스 ID('getpid()')와 부모 프로세스 ID('getppid()')를 출력합니다.

4. 부모 프로세스 코드:
   if (pid > 0) {
       printf("Hello from the parent process! PID: %d\n", getpid());
       printf("Parent's child PID: %d\n", pid);
   }

   - 'pid'가 양수이면 부모 프로세스임을 의미합니다. 부모 프로세스는 자신의 프로세스 ID와 자식 프로세스 ID를 출력합니다.

- 'fork()' 호출 후 부모와 자식 프로세스는 동일한 메모리 공간의 복사본을 가지지만, 독립적으로 실행됩니다.
- 자식 프로세스는 부모의 모든 데이터, 열린 파일 디스크립터, 환경 변수 등을 복사받습니다.
- 'fork()'는 일반적으로 프로세스 간의 병렬 처리를 구현하거나, 부모 프로세스가 여러 자식 프로세스를 생성하여 작업을 분담하도록 하는 데 사용됩니다.

예제

다음은 'fork()'를 사용하여 부모 프로세스가 자식 프로세스의 종료를 기다리는 예제입니다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
    pid_t pid;

    pid = fork();

    if (pid == -1) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {
        // 자식 프로세스
        printf("Child process PID: %d\n", getpid());
        sleep(2);  // 작업 수행 (예: 2초 동안 대기)
        printf("Child process exiting\n");
        exit(0);
    } else {
        // 부모 프로세스
        int status;
        printf("Parent process waiting for child to finish\n");
        wait(&status);  // 자식 프로세스가 종료될 때까지 대기
        printf("Child process finished with status: %d\n", WEXITSTATUS(status));
    }

    return 0;
}

 

1. 'wait()' 호출:
   wait(&status);
   - 부모 프로세스는 'wait()'를 호출하여 자식 프로세스가 종료될 때까지 대기합니다.
   - 자식 프로세스가 종료되면, 자식의 종료 상태가 'status' 변수에 저장됩니다.

2. 종료 상태 확인:
   printf("Child process finished with status: %d\n", WEXITSTATUS(status));
   - 'WEXITSTATUS(status)' 매크로를 사용하여 자식 프로세스의 종료 상태를 확인합니다.

 'fork()'와 'wait()'를 조합하여 부모 프로세스가 자식 프로세스의 종료를 기다리는 방식으로 프로세스 간의 동기화를 할 수 있습니다.

 

쓰레드를 사용하지 않고, fork를 사용하는 이유

 

`fork()`와 스레드를 사용하는 것은 각각 다른 상황에 적합합니다. `fork()`를 사용하는 이유는 다음과 같습니다:

1. 독립적인 프로세스 생성: `fork()`를 사용하면 부모 프로세스가 자식 프로세스를 생성할 수 있습니다. 이 자식 프로세스는 완전히 독립적인 메모리 공간을 갖고 새로운 프로그램으로 실행됩니다. 이는 부모와 자식 간의 상호작용이 필요하지 않거나, 부모와 자식 간의 데이터 공유를 피해야 하는 경우에 유용합니다.

2. 단순한 병렬 처리: 여러 작업을 병렬로 처리할 때, 각 작업을 별도의 프로세스로 실행하여 서로 간섭 없이 실행되도록 할 수 있습니다. 이는 여러 CPU 코어를 활용하여 작업을 분산시키는 데 유용합니다.

3. 안전한 병렬 처리: 스레드는 동일한 프로세스 내에서 실행되므로, 하나의 스레드가 메모리를 손상시키면 다른 스레드도 영향을 받을 수 있습니다. 하지만 `fork()`를 사용하여 각 작업을 별도의 프로세스로 분리하면, 각 프로세스가 독립적인 메모리 공간을 갖기 때문에 안전합니다.

4. 자원 공유 회피: 스레드를 사용하면 메모리 공유가 발생할 수 있으며, 이는 동기화 문제를 일으킬 수 있습니다. 하지만 `fork()`를 사용하여 프로세스를 분리하면 각 프로세스가 자체적인 메모리 공간을 갖기 때문에 이러한 문제를 피할 수 있습니다.

5. 구조적인 분리: 각 작업을 별도의 프로세스로 실행하면 코드를 논리적으로 분리할 수 있습니다. 이는 복잡한 프로그램을 개발하거나 유지보수하는 데 도움이 됩니다.

그러나 스레드를 사용하는 것이 `fork()`보다 더 효율적인 경우도 있습니다. 예를 들어, 스레드를 사용하면 메모리를 공유할 수 있어서 자원 소모가 줄어들고, 스레드 간의 통신이 더욱 빠르게 이루어질 수 있습니다. 따라서 상황에 따라 적절한 기술을 선택하는 것이 중요합니다.

 

 

우분투에 쓰레드가 있나?

네, 우분투 및 기타 리눅스 기반 시스템은 POSIX 스레드 (Pthreads)를 지원합니다. Pthreads는 여러 개의 실행 스레드를 생성하고 관리하는 데 사용되는 표준 스레드 라이브러리입니다. 이를 사용하여 멀티스레드 프로그램을 작성할 수 있습니다.

Pthreads 라이브러리

Pthreads 라이브러리는 다음과 같은 기능을 제공합니다:
1. 스레드 생성 및 종료: 새로운 스레드를 생성하거나 종료할 수 있습니다.
2. 스레드 동기화: 스레드 간의 동기화를 위한 뮤텍스, 조건 변수, 세마포어 등을 제공합니다.
3. 스레드 관리: 스레드의 우선순위 설정, 상태 확인 등의 기능을 제공합니다.

예제

다음은 간단한 Pthreads 예제입니다. 이 예제는 두 개의 스레드를 생성하여 각각 다른 작업을 수행합니다.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// 첫 번째 스레드가 실행하는 함수
void *thread_function1(void *arg) {
    printf("Thread 1 is running\n");
    return NULL;
}

// 두 번째 스레드가 실행하는 함수
void *thread_function2(void *arg) {
    printf("Thread 2 is running\n");
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    int result;

    // 첫 번째 스레드 생성
    result = pthread_create(&thread1, NULL, thread_function1, NULL);
    if (result != 0) {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }

    // 두 번째 스레드 생성
    result = pthread_create(&thread2, NULL, thread_function2, NULL);
    if (result != 0) {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }

    // 첫 번째 스레드가 종료될 때까지 대기
    result = pthread_join(thread1, NULL);
    if (result != 0) {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }

    // 두 번째 스레드가 종료될 때까지 대기
    result = pthread_join(thread2, NULL);
    if (result != 0) {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }

    printf("Both threads have finished\n");

    return 0;
}

이 예제에서는 두 개의 스레드를 생성하여 각각 `thread_function1`과 `thread_function2`를 실행합니다. `pthread_create` 함수를 사용하여 스레드를 생성하고, `pthread_join` 함수를 사용하여 스레드가 종료될 때까지 대기합니다.

- 우분투 및 기타 리눅스 배포판은 POSIX 스레드를 지원하여 멀티스레드 프로그래밍을 할 수 있습니다.
- Pthreads 라이브러리를 사용하여 스레드를 생성하고 관리할 수 있습니다.
- 스레드는 프로세스 내에서 병렬적으로 실행되는 실행 단위이며, 멀티스레드 프로그래밍은 병렬 처리를 위한 강력한 도구입니다.

 

 

프로그램을 2개 실행시키는것과 fork하는것의 차이

프로그램을 두 번 실행시키는 것과 `fork()`를 사용하여 프로세스를 생성하는 것 사이에는 중요한 차이가 있습니다. 

1. 프로세스 생성 방법:
   - 프로그램을 두 번 실행시키는 경우: 단순히 터미널 또는 셸에서 두 번 명령을 실행하여 두 개의 프로세스를 시작합니다. 이 경우 두 프로세스는 서로 독립적으로 실행됩니다.
   - `fork()`를 사용하는 경우: 하나의 프로세스에서 `fork()`를 호출하여 새로운 프로세스를 생성합니다. 부모 프로세스와 자식 프로세스가 생성되며, 이들은 부모-자식 관계를 가집니다.

2. 프로세스 간의 관계:
   - 프로그램을 두 번 실행시키는 경우: 각 프로그램은 서로 독립적으로 실행되므로 부모-자식 관계가 없습니다.
   - `fork()`를 사용하는 경우: 부모 프로세스와 자식 프로세스는 서로 관련이 있습니다. 자식 프로세스는 부모 프로세스의 복사본이므로, 부모 프로세스에서 생성된 자식 프로세스가 됩니다.

3. 프로세스 간의 통신:
   - 프로그램을 두 번 실행시키는 경우: 두 프로세스는 완전히 독립적이기 때문에 직접적인 통신이 어렵습니다. 다만 운영 체제가 제공하는 IPC(Inter-Process Communication) 기법을 사용하여 통신할 수는 있습니다.
   - `fork()`를 사용하는 경우: 부모 프로세스와 자식 프로세스는 같은 프로그램의 복사본이므로, 메모리를 공유할 수 있습니다. 이를 통해 두 프로세스 간에 데이터를 공유하거나 통신할 수 있습니다.

4. 시스템 자원 사용:
   - 프로그램을 두 번 실행시키는 경우: 두 프로세스는 각각 독립적으로 메모리와 CPU 시간을 사용하므로, 시스템 리소스를 두 배로 사용합니다.
   - `fork()`를 사용하는 경우: `fork()`를 호출하여 새로운 프로세스를 생성할 때, 부모 프로세스와 자식 프로세스는 메모리를 공유하게 됩니다. 이러한 공유는 리소스 사용량을 절약할 수 있습니다.

따라서 프로그램을 두 번 실행시키는 것과 `fork()`를 사용하여 프로세스를 생성하는 것은 각각의 상황에 따라 적합한 선택이 될 수 있습니다. 프로세스 간의 관계를 유지하고 통신이 필요한 경우에는 `fork()`를 사용하는 것이 더 적합할 수 있습니다.

 

 

win32에도 fork가 있나?

Win32 플랫폼에는 POSIX 시스템에서 사용되는 것과 동일한 `fork()` 함수는 없습니다. 대신에, Windows에서는 프로세스를 생성하기 위해 `CreateProcess()` 함수를 사용합니다. 

`CreateProcess()` 함수는 새로운 프로세스를 생성하고 해당 프로세스에 대한 핸들을 반환합니다. 이를 통해 새로운 프로세스의 실행을 시작할 수 있습니다.

유닉스 시스템의 `fork()` 함수와는 달리, `CreateProcess()` 함수는 새로운 프로세스의 주소 공간을 부모 프로세스와 별도로 생성하지 않습니다. 대신에, 새로운 프로세스의 이미지를 로드하여 실행하는 방식으로 동작합니다.

따라서 Win32 플랫폼에서는 `fork()`와 같은 동작을 달성하기 위해 `CreateProcess()` 함수를 사용해야 합니다.

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함