카테고리
게시일
Dec 2, 2025
fork()란?
현재 프로세스를 그대로 복제해 새 프로세스를 만드는 시스템콜
부모와 자식은 같은 코드와 메모리 구조로 시작한다.
실제 메모리는 Copy-on-Write로 필요할 때만 복사된다.
fork() 이후에는 부모와 자식이 동시에 실행된다.- fork는 자식 프로세스에 0 반환, 부모에는 자식 PID 반환한다. 이걸로 구분한다.
언제 쓰이냐?
- 네트워크 요청을 처리
요청이 들어오면 병렬 프로세스가 각 요청을 처리하도록 설계. 한 요청을 처리하다 프로세스가 죽어도 메인 프로세스는 살아남아 전체 프로그램이 죽는 것을 방지한다.
- 완전히 새로운 프로세스를 띄울 때 (후술)
실제로는 네트워크 요청 처리에는 Thread를 더 많이 쓰고, 개발자는 이런 fork나 thread, event loop 같은 것들이 추상화된 상위 라이브러리나 기술을 쓰기 때문에 직접 쓸 일은 거의 없다. 원리상 알고있을 것.
exec()란?
현재 프로세스의 내용을 완전히 다른 프로그램으로 교체하는 시스템콜
새로운 프로세스를 만드는게 아니고, 기존 프로세스를 다른 프로그램으로 교체하는 것이다.
프로세스 ID는 유지되고, 주소 공간만 바뀐다.
새 프로그램은 처음부터 실행된다.
리눅스 쉘에서 프로그램을 실행하면 일어나는 일
모든 리눅스시스템의 프로세스 실행은
- fork() 로 프로세스 복사
- Exec()로 실행
의 과정을 거친다.
쉘도 마찬가지로, 우선 쉘에서 프로세스를 실행하면 fork()에서 기존 쉘의 가상메모리 공간을 모두 복사해서 새로운 프로세스를 만든다. fork()가 실제로 메모리를 복사하면 너무 낭비다. 그래서 실제로는 복사된 것처럼 보이게 한다.
Copy on Write
자식 프로세스에서 부모 프로세스의 메모리 공간을 보이게 하는 것 처럼 하려면, page table을 그대로 유지한 채 복사하면 된다. 이후 프로세스가 메모리에 접근해 데이터를 바꾸면, 그 때 해당 페이지를 새로 할당하고 메모리 내용을 복사(Copy)한다. 이것이 Copy On Write이다. 일단 페이지테이블을 Copy하기 때문에 Copy On Write이다.
Exec()를 실행하면?
- 이후 exec를 실행하면 syscall에서 모든 page table이 리셋된다. (mm_struct를 교체한다.)
- syscall은 ld.so(로더)와 프로그램 ELF를 메모리에 매핑한다.
- 이후 syscall은 프로그램 시작점을 로더로 잡는다.
- 로더는 유저스페이스에서 동작하며, 동적 라이브러리 로드
- 로더에서 main()에 진입하며 로드가 끝난다.
tip. 왜 로더는 유저 공간에 매핑되어있나?
syscall에서 심볼 연결까지 다 해주면 되는 것 아닌가? 왜 불편하게 유저공간 로더까지 불러오느냐?
로더가 엄청나게 종류가 많다. ABI에 따라 수많은 로더를 가지고 있다.
그 많은 로더 구현체를 커널 코드에서 다 가지고 있을 수 없다.