fork()와 exec()는 유닉스/리눅스에서 새 프로세스를 생성하는 두 가지 시스템 콜이다. fork() 는 현재 프로세스를 복제하고, exec() 는 현재 프로세스 이미지를 새 프로그램으로 교체한다.
fork()
c
# include <unistd.h>
pid_t pid = fork();
if (pid < 0) {
// 오류
} else if (pid == 0) {
// 자식 프로세스: pid = 0
printf("Child PID: %d\n", getpid());
execl("/bin/ls", "ls", "-l", NULL); // 보통 exec으로 교체
} else {
// 부모 프로세스: pid = 자식 PID
printf("Parent, child PID: %d\n", pid);
wait(NULL); // 자식 종료 대기
}
fork() 후 부모와 자식은 처음에 메모리 페이지를 공유한다. 어느 쪽이 수정을 시도하는 순간에만 페이지를 실제로 복사한다 → fork() 비용 최소화.
exec 패밀리
c
execl("/bin/ls", "ls", "-la", NULL); // 경로 + 인자 목록
execlp("ls", "ls", "-la", NULL); // PATH 검색
execv("/bin/ls", argv); // 인자 배열
execvp("ls", argv); // PATH + 배열
execve("/bin/ls", argv, envp); // 환경변수 명시
exec 호출 성공 시 기존 코드/데이터/스택이 모두 새 프로그램으로 교체되어 반환되지 않는다.
python
import subprocess
# 내부적으로 fork + exec 사용
result = subprocess.run(
["ls", "-la"],
capture_output=True, text=True
)
print(result.stdout)
fork 없는 vfork()
exec()를 즉시 호출할 목적으로만 사용하는 최적화된 변형. 부모의 페이지 테이블도 공유해 더 빠르지만, exec 전까지 부모가 블록된다.
관련 개념
- •프로세스와 스레드 — fork가 생성하는 단위
- •시스템 콜 — fork/exec의 인터페이스
- •IPC — fork 후 부모-자식 간 통신
- •POSIX 시그널 — 자식 종료 시 SIGCHLD