오늘은 fork()와 함께 사용되는 또 다른 핵심 시스템 콜인 execve()
- execve()는 현재 프로세스를 완전히 다른 프로그램으로 바꿔버리는 강력한 기능으로, 유닉스/리눅스 시스템에서 새로운 프로그램을 실행하는 핵심 메커니즘
execve() 시스템 콜이란?
execve()는 현재 실행 중인 프로세스를 완전히 다른 프로그램으로 교체하는 시스템 콜
출처: linux-in-practice GitHub 위의 소스코드를 분석한 내용을 정리하였습니다!
예제 코드 분석
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
static void child()
{
char *args[] = { "/bin/echo", "hello" , NULL};
printf("I'm child! my pid is %d.\n", getpid());
fflush(stdout);
execve("/bin/echo", args, NULL);
err(EXIT_FAILURE, "exec() failed");
}
static void parent(pid_t pid_c)
{
printf("I'm parent! my pid is %d and the pid of my child is %d.\n",
getpid(), pid_c);
exit(EXIT_SUCCESS);
}
int main(void)
{
pid_t ret;
ret = fork();
if (ret == -1)
err(EXIT_FAILURE, "fork() failed");
if (ret == 0) {
// child process came here because fork() returns 0 for child process
child();
} else {
// parent process came here because fork() returns the pid of newly created child process (> 1)
parent(ret);
}
// shouldn't reach here
err(EXIT_FAILURE, "shouldn't reach here");
}
함수 원형
int execve(const char *pathname, char *const argv[], char *const envp[]);
- pathname: 실행할 프로그램 경로 (예: "/bin/echo")
- argv[]: 프로그램에 전달할 인자 배열
- envp[]: 환경 변수 (NULL 가능)
fork() + execve() 조합
코드 분석
static void child()
{
char *args[] = { "/bin/echo", "hello" , NULL};
printf("I'm child! my pid is %d.\n", getpid());
fflush(stdout); // 버퍼 강제 출력
execve("/bin/echo", args, NULL);
err(EXIT_FAILURE, "exec() failed"); // 이 줄은 실행되지 않음
}
실행 과정
- fork() 호출: 부모/자식 프로세스 생성
- 자식 프로세스: child() 함수 실행
- printf 출력: "I'm child! my pid is 22913."
- execve() 호출: 자식 프로세스가 /bin/echo 프로그램으로 완전 교체
- echo 실행: "hello" 출력 후 종료
왜 err() 줄이 실행되지 않을까?
execve()가 성공하면:
- 기존 프로그램 코드가 완전히 사라짐
- 새로운 프로그램(/bin/echo)이 메모리에 로드됨
- 원래 코드의 다음 줄은 존재하지 않게 됨
즉, execve() 성공 시 리턴하지 않습니다!
실행 결과 비교
fork()만 사용한 경우:
I'm parent! my pid is 21301 and the pid of my child is 21302.
I'm child! my pid is 21302.
fork() + execve() 사용한 경우:
I'm parent! my pid is 22912 and the pid of my child is 22913.
I'm child! my pid is 22913.
hello
핵심 개념 정리
fork() vs execve()
- fork(): 프로세스 복제 (같은 프로그램이 2개)
- execve(): 프로세스 교체 (다른 프로그램으로 변환)
프로세스 생명주기
부모 프로세스
↓ fork()
부모 + 자식 프로세스 (같은 프로그램)
↓ execve() (자식만)
부모 프로세스 + 새로운 프로그램을 실행하는 자식
왜 이런 설계를 사용할까?
Unix/Linux에서 새 프로그램을 실행하는 표준 방법:
- fork(): 새 프로세스 공간 확보
- execve(): 원하는 프로그램으로 교체
이 방식의 장점:
- 프로세스 관리의 일관성
- 부모-자식 관계 유지
- 파일 디스크립터 상속 가능
strace로 확인하기
strace ./fork-and-exec
시스템 콜 레벨에서 clone()과 execve()가 어떻게 호출되는지 확인할 수 있습니다.
주요 함수들
- fflush(stdout): 출력 버퍼를 즉시 비워 화면에 출력
- EXIT_SUCCESS: 성공적 종료를 의미하는 상수 (값: 0)
- EXIT_FAILURE: 실패를 의미하는 상수 (값: 1)
마무리
execve()는 fork()와 함께 유닉스/리눅스 시스템의 프로세스 생성과 실행의 핵심을 이루는 시스템 콜.
이 두 시스템 콜의 조합을 이해하면 쉘이 어떻게 명령어를 실행하는지, 데몬 프로세스가 어떻게 생성되는지 등 시스템 프로그래밍의 많은 개념을 이해할 수 있음.
'system' 카테고리의 다른 글
[시스템 프로그래밍] Tiny Shell 프로젝트: 잡 컨트롤, 시그널, 레이스 컨디션 다루기 (0) | 2025.09.09 |
---|---|
[시스템 프로그래밍] execve() 시스템 콜 C vs 어셈블리 비교 분석 (0) | 2025.09.08 |
[시스템 프로그래밍] fork() 시스템 콜로 이해하는 프로세스 복제 원리 (0) | 2025.09.08 |
[시스템 보안] 버퍼 오버플로우 (0) | 2025.09.08 |
[시스템 프로그래밍] fork()와 execve()로 이해하는 bash의 명령어 실행 원리 (0) | 2025.09.08 |