fork 는 일반적으로 새로운 프로세스를 생성하기 위해서 사용하는 시스템 콜중 하나이다. fork 를 호출한 프로세스를 부모 프로세스라 부르고 fork 에 의해서 생성된 새로운 프로세스를 자식 프로세스라고 부른다.
예전 Unix 에서는 자식 프로세스가 생성되면서 부모 프로세스의 메모리, 파일 기술자 테이블 등을 복사하여 프로세스를 구성하였다. 하지만, 자원을 복사하는 데 걸리는 시간 때문에, 프로세스 생성까지 오랜 시간이 걸렸다. 이러한 단점을 보완하기 위해서 대안으로 만들어진 것이 vfork 이다.
vfork 는 부모 프로세스와 자원을 공유한다. 자원 복사가 이루어지지 않기 때문에 기존 fork 보다 빠르게 프로세스를 생성할 수 있다. 하지만 자원을 공유하기 때문에 자원에 대한 race condition 이 발생하지 않도록 해주기 위해서 부모 프로세스는 자식 프로세스가 exit 하거나 execve 가 호출되기 전까지 block 된다.
아래는 kernel 의 do_fork 함수중 일부이다. vfork 로 do_fork 가 호출된 경우에 wati_for_completion 을 호출하고 block 되는 것을 볼 수 있다.
execve 가 수행되면서 프로세스 구조체에서 wait 중인 프로세스가 있는지 확인하고 있는 경우 complete 를 호출하여 위 코드에서 wait 하고 있던 프로세스를 깨워준다.
exit 이 수행되면서 호출되는 mm_release 를 통해서 wait 중인 프로세스가 있는지 확인하고 complete 를 호출하여 wait 하는 프로세스를 깨워준다.
그럼 항상 fork 는 메모리를 복사하기 때문에 프로세스를 생성하기 위해서는 vfork 를 사용해야 하는가 라는 의문이 들 수 있다.
fork 가 항상 메모리를 복사한다는 것은 예전 방식이고, 현재 리눅스에서 fork 는 copy-on-write 기법을 사용하여 프로세스 생성시 모든 자원을 복사하는 것이 아니고 변경사항이 생길 경우에만 복사하도록 구현되어 있다. 따라서 현재 fork 가 갖는 단점은 부모 프로세스의 페이지 테이블을 복사하는 것과 자식 프로세스를 기술하기 위한 프로세스 구조체를 할당받는 시간과 메모리 뿐이다.
vfork 를 사용하는 경우에 execve 를 바로 호출하지 않는 경우라면 자식 프로세스에서 수정한 변수들이 부모 프로세스에 영향을 미칠 수 있기 때문에 주의해서 코딩해야 한다.
참고 사이트
1. http://unius.tistory.com/entry/fork-vs-vfork
2. http://www.unixguide.net/unix/programming/1.1.2.shtml
3. http://book.opensourceproject.org.cn/kernel/kernelpri/opensource/0131181637/ch03lev1sec3.html
예전 Unix 에서는 자식 프로세스가 생성되면서 부모 프로세스의 메모리, 파일 기술자 테이블 등을 복사하여 프로세스를 구성하였다. 하지만, 자원을 복사하는 데 걸리는 시간 때문에, 프로세스 생성까지 오랜 시간이 걸렸다. 이러한 단점을 보완하기 위해서 대안으로 만들어진 것이 vfork 이다.
vfork 는 부모 프로세스와 자원을 공유한다. 자원 복사가 이루어지지 않기 때문에 기존 fork 보다 빠르게 프로세스를 생성할 수 있다. 하지만 자원을 공유하기 때문에 자원에 대한 race condition 이 발생하지 않도록 해주기 위해서 부모 프로세스는 자식 프로세스가 exit 하거나 execve 가 호출되기 전까지 block 된다.
아래는 kernel 의 do_fork 함수중 일부이다. vfork 로 do_fork 가 호출된 경우에 wati_for_completion 을 호출하고 block 되는 것을 볼 수 있다.
kernel/fork.c do_fork:
(생략)
1398 if (clone_flags & CLONE_VFORK) {
1399 p->vfork_done = &vfork;
1400 init_completion(&vfork);
1401 }
(생략)
1421 if (clone_flags & CLONE_VFORK) {
1422 wait_for_completion(&vfork);
1423 if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) {
1424 current->ptrace_message = nr;
1425 ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
1426 }
1427 }
(생략)
(생략)
1398 if (clone_flags & CLONE_VFORK) {
1399 p->vfork_done = &vfork;
1400 init_completion(&vfork);
1401 }
(생략)
1421 if (clone_flags & CLONE_VFORK) {
1422 wait_for_completion(&vfork);
1423 if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) {
1424 current->ptrace_message = nr;
1425 ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
1426 }
1427 }
(생략)
execve 가 수행되면서 프로세스 구조체에서 wait 중인 프로세스가 있는지 확인하고 있는 경우 complete 를 호출하여 위 코드에서 wait 하고 있던 프로세스를 깨워준다.
fs/exec.c coredump_wait :
(생략)
1447 vfork_done = tsk->vfork_done;
1448 if (vfork_done) {
1449 tsk->vfork_done = NULL;
1450 complete(vfork_done);
1451 }
1452
(생략)
(생략)
1447 vfork_done = tsk->vfork_done;
1448 if (vfork_done) {
1449 tsk->vfork_done = NULL;
1450 complete(vfork_done);
1451 }
1452
(생략)
exit 이 수행되면서 호출되는 mm_release 를 통해서 wait 중인 프로세스가 있는지 확인하고 complete 를 호출하여 wait 하는 프로세스를 깨워준다.
kernel/fork.c mm_release :
463 struct completion *vfork_done = tsk->vfork_done;
(생략)
469 if (vfork_done) {
470 tsk->vfork_done = NULL;
471 complete(vfork_done);
472 }
463 struct completion *vfork_done = tsk->vfork_done;
(생략)
469 if (vfork_done) {
470 tsk->vfork_done = NULL;
471 complete(vfork_done);
472 }
그럼 항상 fork 는 메모리를 복사하기 때문에 프로세스를 생성하기 위해서는 vfork 를 사용해야 하는가 라는 의문이 들 수 있다.
fork 가 항상 메모리를 복사한다는 것은 예전 방식이고, 현재 리눅스에서 fork 는 copy-on-write 기법을 사용하여 프로세스 생성시 모든 자원을 복사하는 것이 아니고 변경사항이 생길 경우에만 복사하도록 구현되어 있다. 따라서 현재 fork 가 갖는 단점은 부모 프로세스의 페이지 테이블을 복사하는 것과 자식 프로세스를 기술하기 위한 프로세스 구조체를 할당받는 시간과 메모리 뿐이다.
vfork 를 사용하는 경우에 execve 를 바로 호출하지 않는 경우라면 자식 프로세스에서 수정한 변수들이 부모 프로세스에 영향을 미칠 수 있기 때문에 주의해서 코딩해야 한다.
참고 사이트
1. http://unius.tistory.com/entry/fork-vs-vfork
2. http://www.unixguide.net/unix/programming/1.1.2.shtml
3. http://book.opensourceproject.org.cn/kernel/kernelpri/opensource/0131181637/ch03lev1sec3.html
'IT 기술 > 리눅스 커널' 카테고리의 다른 글
sparse : kernel static analysis tool (0) | 2013.02.12 |
---|---|
커널 로컬 버전 세팅 (0) | 2012.11.09 |
Linux kernel CPU Frequency 변경(DVFS) 코드 (2) | 2011.02.11 |
kjournald 에 IPPRIO_CLASS_RT 권한 부여 (0) | 2010.05.03 |
리눅스 2.6 pdflush VS. 리눅스 2.4 bdflush, kupdate (1) | 2009.09.04 |