IT 이야기/IT Tech

fork() vs vfork()

필넷 2009. 6. 14. 13:44
반응형

유닉스에서는 새로운 프로세스를 생성하기 위해서 fork() 함수를 호출합니다. 호출을 통해 새로 생성된 프로세스를 흔히 자식 프로세스(Child Process)라고 하며 호출한 프로세스를 부모 프로세스(Parent Process)라고 합니다.

fork() 함수는 특성상 한번 호출을 통해 부모 프로세스와 자식 프로세스에게 두번 리턴되어지게 됩니다. 부모 프로세스에게는 자식 프로세스의 ID를 리턴하고, 자식 프로세스에게는 0을 리턴합니다. 자식 프로세스는 부모의 PID를 알고 싶을때는 getppid()를 호출할 수 있습니다.

따라서 다음과 같이 코드를 작성할 수가 있습니다.

#include 
#include  
int main(void)
{
   int temp = 10;
   pid_t pid;
   if ((pid = fork()) < 0) {
      printf("fork() error \n");
   } else if (pid == 0) {    /* 자식 프로세스 */
      temp ++;
   } else {                    /* 부모 프로세스 */
      sleep(2);
   }
   printf("pid = %d, temp = %d", getpid(), temp);   /* 부모와 자식 프로세스 모두 출력 */
}

보통 fork() 함수의 호출을 통해 자식 프로세스의 데이터 부분은 부모 프로세스의 데이터 부분의 복사본을 갖게 됩니다. I/O 역시도 동일하게 중복(dup()을 사용한 것과 동일함)되어 사용되기 때문에, 부모와 자식 프로세스간의 wait()의 호출과 같은 동기화 없이 부모와 자식이 표준출력을 통해 쓰기를 한다면 뒤섞이게 됩니다. 

fork()의 일반적인 사용형태를 보면 fork() 이후에 exec() 의 호출을 통해 새로운 바이너리로 교체하여 수행을 합니다. 따라서 fork()를 통해 부모 프로세스의 복사본을 만드는 작업은 불필요한 오버헤드가 됩니다.  이런 경우에 사용하는 시스템 호출이 바로 vfork()입니다.

vfork()는 부모 프로세스의 복사본을 만들지 않고, exec() 나 exit()와 같은 호출이 발생할때까지 부모 프로세스의 데이터 부분 참조를 통해 부모 프로세스의 주소공간에서 동작하게 됩니다. 

위의 첫번째 예제를 아래와 같이 바꾸어 실행한다면 부모 프로세스에의 주소공간에서 동작하는 것을 확인할 수 있습니다.

#include 
#include  
int main(void)
{
   int temp = 10;
   pid_t pid;
   if ((pid = vfork()) < 0) {
      printf("vfork() error \n");
   } else if (pid == 0) {    /* 자식 프로세스 */
      temp ++;
      _exit(0);      /* 자식 프로세스 종료 */
   } else {          /* 부모 프로세스 */
      sleep(2);     /* 자식 프로세스에서 exit()을 호출할때까지 부모 프로세스는 블럭되므로 필요없다 */
   }
   printf("pid = %d, temp = %d", getpid(), temp);    /* 부모 프로세스만 출력됨 */
}

첫번째 예제에서는 부모와 자식 프로세스에서 출력한 temp 변수의 값이 다르지만 즉, 자식 프로세스가 변경한 temp 변수의 값이 부모 프로세스에 영향을 미치지 않습니다. 그러나 두번째 예제에서는 자식이 변경한 temp 변수의 값이 부모 프로세스에서 출력되는 것을 확인할 수 있습니다.

이러한 차이점으로 인해, 자식 프로세스가 exec()나 exit()를 호출하기전에 부모 프로세스에 의존적인 상황이 있다면 두 프로세스 모두 교착상태에 빠질수 있는 잠재적인 위험이 있으므로 사용에 주의해야합니다.

하지만, 현재의 많은 시스템에서 fork()의 효율성을 높이기 위해서 COW(copy on write) 매커니즘[각주:1]을 사용하고 있기때문에, vfork()보다는 fork()의 사용이 권장되고 있습니다.

  1. vfork() 처럼 부모 프로세스의 데이터 부분에 대한 참조만 소유하고 있다가 실제 변경이 발생하는 시점에 복사가 이루어진다. [본문으로]
반응형