fork와 vfork

fork는 새로운 프로세스를 만드는 리눅스 시스템 콜이다. fork로 인해 생성된 자식 프로세스와 부모 프로세스는 서로 별개의 메모리영역을 가진다. 하지만 자식프로세스가 생성될 때 아예 부모의 메모리를 copy하는 것은 아니고 실제로는 부모와 자식이 page를 공유하다가 부모,자식중 누군가가 특정 page에 대해 쓰기작업을 할 때 그 누군가를 위한 page의 copy가 발생한다. 
그러면 부모 자식관계가 여러명이면 어떻게 될까?
그래도 똑같다. 커널 내부적으로 fork에 의해 맺어진 프로세스들 사이에 page의 공유에 대한 정보가 저장되있을테고 그 프로세스들중 한 명만 write를 하면 그 한 명을 위해 copy가 발생하고 이제 그 프로세스는 다른 page를 사용하게 되는 것이다.

vfork는 예전에 fork가 내부적으로 COW를 사용하지 않을 때를 위해서 만들어졌다고 한다. (vfork는 vfork + execve 를 위한 fork의 최적화 버전이라고 한다.)
vfork는 마치 clone처럼 부모와 자식이 같은 메모리영역을 공유한다. (하지만 스택조차도 공유한다.) 하지만 vfork를 하게 되면 부모 process는 자식 process가 종료되거나 exec* 함수를 호출할 때 다시 수행이 재개된다. 이 때 주의할 점이 자식 process가 return혹은 exit함수를 통해 종료되서는 안되고, 무조건 _exit함수를 통해 종료되어야 한다는 점이다. 왜냐하면 vfork는 자식process가 부모process의 스택조차도 그대로 사용하기 때문에 자식process가 스택을 어지럽히면 부모process의 실행이 이상할 수 있기 때문이다. return을 통해 vfork가 실행됐던 함수를 빠져나가게 되면 스택이 해제될 테고, exit을 통해 종료하게 되면, atexit을 통해 등록한 함수가 실행되고 cleanup코드들이 실행되면서 스택을 어지럽히게 된다. 따라서 vfork를 통해 실행된 자식process는 반드시 _exit을 통해 종료해야한다.
근데 fork도 이제 COW를 적용하므로 vfork를 사용할 이유가 없지 않나라고 생각할 수 있지만 그래도 몇 가지 차이점때문에 vfork + exec*에 대해서는 vfork가 fork보다 좋다. fork + exec* 의 경우에도 fork시에 자식 process를 위한 page table등이 생성되지만 vfork의 경우에는 마치 thread처럼 page table등에 대해서도 부모꺼를 그대로 씀으로 속도가 빠르다고 할 수 있다. 그리고 fork는 memory가 enough하지 않을 때 실패할 수 있는데 vfork는 이런 문제점에 대해서 자유롭다고 한다. 하지만 vfork는 보안상 안전하지 않고 호환성을 위해 남아있는 것이기 때문에 사용하지 말아야한다. 대신에 대체제로서 생긴 posix_spawn이라는 것을 사용하면 fork + exec*의 문제점들을 해결하면서 효율적으로 vfork를 대체할 수 있다. 

댓글