프로세스 생성 과정
- 0번 프로세스 생성 과정
- .exe 파일 실행
→ 운영체제는 해당 프로그램의 코드 영역과 데이터 영역을 메모리에 로드함
→ 빈 스택과 힙 공간을 확보함
→ 이 프로세스를 관리하기 위한 PCB를 생성하고 값을 초기화함
- .exe 파일 실행
- 이 과정은 운영체제가 부팅되고 0번 프로세스가 생성될 때 딱 1번 실행됨
→ 나머지 모든 프로세스는 0번 프로세스를 복사해서 사용함
→ 복사는 fork() 함수를 이용함 (새로 만드는 것보다 복사가 더 빠름)
부모 / 자식 프로세스
자식 프로세스
: 0번 프로세스를 복사해서 만든 프로세스
- 자식 프로세스는 부모 프로세스의 code, data, stack 영역과 PCB 내용을 전부 복사함
부모 프로세스
: 자식 프로세스 입장에서 0번 프로세스
- 두 프로세스(부모, 자식)는 CPU 스케줄링에 따라 실행됨
→ 어떤 프로세스가 먼저 실행될지는 운영체제가 결정함
프로세스 제어 (Process Control)
Q. 0번 프로세스를 복사하면 똑같이 실행되는 것 아닌가?
→ A. 맞음
Q. 그럼 자기가 원하는 코드는 어떻게 실행시키는가?
→ A. exec() 함수를 실행시켜서 기존 부모의 code/data 영역을 원하는 코드로 덮어씀
fork() 함수
: 현재 실행 중인 프로세스를 복사해서 똑같은 새로운 프로세스를 하나 더 만드는 함수
- 부모 프로세스: 1(0이 아닌 값)을 반환
- 자식 프로세스: 0을 반환
wait() 함수
: 자식 프로세스에게서 exit() 신호가 올 때까지 기다리는 시스템 함수
- 컨텍스트 스위칭을 통해 부모 프로세스에게 CPU가 할당되더라도
자식 프로세스의 종료 신호가 오기 전까지는 다른 코드를 실행하지 않음
execlp() 함수
: 지정한 프로그램를 실행시키는 함수
- 지정한 프로그램의 코드와 데이터를 가져와서 자식 프로세스를 덮어씀
⇒ 이때, 자식 프로세스는 부모와 완전히 다른 프로세스가 됨 - 실패 시 -1 반환, 성공 시 반환값 X
exit() 함수
= 자식 프로세스가 부모 프로세스에게 프로세스의 정상 종료를 알리는 함수
- 부모 프로세스는 자식 프로세스의 Exit Status를 읽고 자식 프로세스를 정리함
- 만약 부모가 먼저 종료되거나 자식이 비정상적으로 종료되어 exit() 신호를 보내지 못하면
→ Exit Status를 읽지 못해 메모리에 계속 살아있게 됨
⇒ 좀비 프로세스 발생
실행 흐름
- fork() 호출
→ 자식 프로세스 생성(복사)
→ 반환값으로 부모 프로세스엔 0이 아닌 값을, 자식 프로세스엔 0을 반환 - fork() 함수의 반환값으로 조건문 분기 실행
- 자식 프로세스
- execlp() 호출
- 만약 실패하면, exit(1)로 비정상 종료
- 부모 프로세스
- wait() 호출 → 자식이 exit()하거나 exec()한 프로그램이 끝날 때까지 기다림
- 자식이 종료되면 wait()이 반환됨 → 자식 프로세스 종료 확인
- 부모도 return 0;으로 종료
int main() {
int pid = fork(); // 부모 → 자식 복사
// 자식 프로세스
if (pid == 0) {
// 자식 프로세스 실행
execlp("ls", "ls", "-l", NULL);
// exec 실행 실패 시 실행
exit(1);
}
// 부모 프로세스
else if (pid > 0) {
// 부모 프로세스 실행
int status;
wait(&status); // 자식이 끝날 때까지 기다림
}
else {
// fork 실패
return 1;
}
return 0;
}