[23-01/운영체제] 프로세스와 프로세스 관리
1. 프로세스 개요
프로세스 개요
■ 프로그램 (Program)
- 하드디스크 등의 저장 매체에 저장. 실행 파일의 형태
■ 프로세스 (Process)
- 프로그램이 메모리에 적재되어 실행 중인 상태
- 필요한 모든 자원 할당 받음
- 자원 : 코드 공간, 데이터 공간, 스택 공간, 힙 공간 - 메모리
● 프로세스의 특징
- 운영체제는 프로그램을 메모리에 적재하고 프로세스로 다룸
- 운영체제는 프로세스에게 실행에 필요한 메모리 할당, 이곳에 코드와 데이터 등 적재
- 프로세스들은 서로 독립적인 메모리 공간을 가짐. 다른 프로세스의 영역에 접근 불허
- 운영체제는 각 프로세스의 메모리 위치와 크기 정보를 관리함.
- 운영체제는 프로세스마다 고유한 번호 (프로세스 ID) 할당
- 프로세스의 관한 모든 정보는 커널에 의해 관리
- 프로세스는 실행 - 대기 - 잠자기 - 대기 - 실행 - 종료 등의 생명 주기를 가짐
- 프로세스 생성, 실행, 대기, 종료 등의 모든 관리는 커널에 의해 수행 ★
프로그램과 프로세스
- 프로세스들은 상호 독립적인 메모리 공간에서 실행
프로세스 관리
- 프로세스의 생성에서 종료까지, 관리는 모두 커널에 의해 이루어짐
- 커널 영역에 프로세스 테이블을 만들고, 프로세스들 목록 관리
- 관리 내용
- 프로세스 생성, 실행, 일시 중단 및 재개, 정보 관리, 프로세스 통신, 프로세스 동기화, 프로세스 중단, 프로세스 컨텍스트 스위칭
프로그램의 다중 인스턴스
■ 한 프로그램을 여러 번 실행시키면 어떻게 될까?
- 프로그램 실행 시 마다 독립된 프로세스 생성 -> 프로세스들을 프로그램의 다중 인스턴스라고 부름
- 각 프로세스에게 독립된 메모리 공간 할당
- 각 프로세스를 별개의 프로세스로 취급
CPU 주소 공간
■ CPU 주소 공간 (CPU address space)
- CPU가 주소선을 통해 액세스할 수 있는 전체 메모리 공간
- 공간 크기
- CPU 주소선의 수에 의해 결정
- 32비트 CPU -> 32개의 주소선 -> 2³²개의 주소 -> 2³² 바이트 -> 4GB 공간
- 1번지의 저장 공간 크기는 1바이트
- 주소 공간은 0번지부터 시작 ~
- CPU 주소 공간보다 큰 메모리? 있어도 액세스 불가능
- CPU 주소 공간보다 작은 양의 메모리? 가능
- CPU가 설치된 메모리의 주소 영역을 넘어 액세스하면 시스템 오류
ex) 32비트 CPU를 가진 컴퓨터 (4GB까지 메모리 액세스 가능)에 2GB의 메모리가 설치되어 있을 때 2GB를 넘어서 액세스하면 없는 메모리를 액세스하므로 심각한 오류 발생
프로세스 구성 - 4개의 메모리 영역 ★
1. 코드 (code) 영역
- 실행될 프로그램 코드가 적재되는 영역
- 사용자가 작성한 모든 함수의 코드
- 사용자가 호출한 라이브러리 함수들의 코드
2. 데이터 (data) 영역
- 프로그램에서 고정적으로 만든 변수 공간
- 전역 변수 공간, 정적 데이터 공간
- 사용자 프로그램과 라이브러리 포함
- 프로세스 적재 시 할당, 종료 시 소멸
3. 힙 (heap) 영역 생성자
- 프로세스의 실행 도중 동적으로 사용할 수 있도록 할당된 공간
- malloc() 등으로 할당받는 공간은 힙 영역에서 할당
- 힙 영역에서 아래 번지로 내려가면서 할당
4. 스택 (stack) 영역
- 함수가 실행될 때 사용될 데이터를 위해 할당된 공간
- 매개변수들, 지역 변수들, 함수 종료 후 돌아갈 주소 등
- 함수는 호출될 때, 스택 영역에서 위쪽으로 공간 할당
- 함수가 return 하면 할당된 공간 반환
- 함수 호출 외에 프로세스에서 필요시 사용 가능
memo: 이론적으로 중요함. 코드 짤 땐 신경 안써도 됨.
프로세스 주소 공간
■ 프로세스 주소 공간
- 프로세스가 실행 중에 접근할 수 있도록 허용된 주소의 최대 범위
- 프로세스 주소 공간은 논리 공간 (가상 공간) 가상 공간 ≠ 가상 메모리
- 0번지에서 시작하여 연속적인 주소
■ 프로세스 주소 공간의 크기
● CPU가 액세스할 수 있는 전체 크기
- 32비트 CPU의 경우 4GB (윈도우, 리눅스 모두 동일)
- 프로세스 주소 공간 크기는 프로세스의 현재 크기와 다름
● 프로세스 주소 공간의 크기
- 프로세스가 액세스할 수 있는 최대 크기 (32비트 CPU 경우 4GB)
● 프로세스의 크기
- 적재된 코드 +
- 전역 변수 +
- 힙 영역에서 현재 할당받은 동적 메모리 공간 +
- 스택 영역에 현재 저장된 데이터 크기
프로세스의 사용자 공간과 커널 공간
■ 프로세스 주소 공간 = 사용자 공간 + 커널 공간 (가상 공간)
● 사용자 공간
- 프로세스의 코드, 데이터, 힙, 스택 영역이 순서대로 할당되는 공간
- 코드와 데이터 영역의 크기는 프로세스 적재 시 결정
- 힙은 데이터 영역 바로 다음부터 시작하고,
- 스택은 사용자 공간의 바닥에서 시작하여 거꾸로 자람
- 힙 영역은 높은 번지로 자라고, 스택은 낮은 번지로 자람
- ex) 처음 malloc(1000)으로 동적 할당받는 공간은 데이터 영역 바로 다음의 힙 시작부분부터 할당
- ex) 처음 함수가 호출될 때 할당되는 스택 공간은 스택 영역 바닥부터 위로 할당
● 커널 공간
- 프로세스가 시스템 호출을 통해 이용하는 커널 공간
- 커널 코드, 커널 데이터, 커널 스택 (커널 코드가 실행될 때)이 존재
■ 결론
- 프로세스의 코드와 데이터는 실행 파일에 결정된 상태로 코드 영역과 데이터 영역에 적재 -> 실행 중에 크기가 변하지 않음
- 프로세스는 사용자 공간의 최대 범위까지 동적할당 받으면서 힙 영역과 스택 영역을 늘려갈 수 있음
(스택 영역: 복귀 주소)
■ 프로세스 주소 공간 사례
커널 공간의 의미
■ 각 프로세스
- 독립된 사용자 공간 소유
- 커널 공간 소유
■ 커널 공간
- 프로세스가 사용자 코드에서 시스템 호출을 통해 커널 코드 실행할 때 커널 공간 사용
- 커널 코드를 실행하고 있는 것은 사용자 프로세스
- 커널 코드가 적재된 물리 메모리의 위치 역시 사용자 프로세스가 소유한 매핑 테이블 사용
■ 사용자 공간과 커널 공간의 결론
- 프로세스마다 각각 사용자 주소 공간이 있음
- 시스템 전체에는 하나의 커널 주소 공간이 있음
- 모든 프로세스는 커널 주소 공간을 공유함
프로세스의 주소 공간은 가상 주소 공간
■ 프로세스의 주소 공간은 가상 공간
● 프로세스가 사용하는 주소는 가상 주소
- 프로세스에서 0번지는 가상 주소 0번지
- 가상 주소는 0번지부터 시작
- 프로세스 내의 코드 주소, 전역 변수에 대한 주소, malloc()에 의해 리턴된 주소, 스택에 담긴 지역 변수의 주소는 모두 가상 주소
● 프로세스의 주소 공간(가상 주소 공간)은 사용자나 개발자가 보는 관점
- 사용자나 개발자는 프로그램이 0번지부터 시작,
- 연속적인 메모리 공간에 형성,
- 최대 크기의 메모리가 설치되어 있다고 상상
● 실제 상황
- 설치된 물리 메모리의 크기는 주소 공간보다 작을 수 있음
- 프로세스의 코드, 데이터, 힙, 스택은 물리 메모리에 흩어져 저장됨 (연속적인 메모리 공간이 아님)
● 프로세스 주소 공간은 각 프로세스마다 주어지는가? Yes
- 프로세스마다 주소 공간은 별개
● 프로세스 주소 공간은 충돌하는가? No
- 프로세스 주소 공간은 가상 주소 공간
- 가상 주소가 물리 주소로 매핑되므로, 물리 메모리에서는 충돌하지 않음
■ 가상 주소 공간의 물리 메모리로의 매핑
- 사용자는 연속적인 공간(가상 주소 공간)으로 생각 -> 그러나 가상 주소의 데이터가 물리 메모리에 분산되어 있어 어느 물리 번지에 있을지 알 수 없음
■ 프로세스들의 가상 주소 공간과 물리 메모리
- 서로 다른 공간 차지하므로 충돌 일어나지 않음
2. 커널의 프로세스 관리
프로세스 테이블과 프로세스 제어 블록 ★
■ 프로세스 테이블 (Process Table)
- 시스템의 모든 프로세스들을 관리하기 위한 표
- 시스템에 한 개만 있음
- 구현방식은 운영체제마다 다름
■ 프로세스 제어 블록 (Process Control Block, PCB)
- 프로세스에 관한 정보를 저장하는 구조체
- 프로세스당 하나씩 존재
- 프로세스가 생성될 때 만들어지고 종료되면 삭제
- 커널에 의해 생성, 저장, 읽혀지는 등 관리 ★
■ 프로세스 테이블과 프로세스 제어 블록 (PCB)
● 위치
- 커널 영역, 커널 코드(커널 모드)만이 액세스 가능
프로세스 제어 블록(PCB)에 저장되는 정보
1. 프로세스 번호 (PID, Process ID) : 0과 양의 정수, 유일한 번호, 이 번호로 프로세스 구분
2. 부모 프로세스 번호 (PPID, Parent Process ID) : 부모 프로세스의 PID
3. 프로세스 상태 (Process State) 정보 : 준비, 실행 중, 블록(입출력 완료대기) 등
4. CPU 컨텍스트 정보
- PC (Program Counter) : 프로세스가 선택되면 실행을 시작할 프로세스 내 코드 주소
- 사용자 모드에 있었던 경우, 사용자 공간의 코드 주소
- 커널 모드에 있었던 경우, 커널 공간의 코드 주소
- SP (Stack Pointer)
- 기타 레지스터
5. 스케줄링 정보
- 우선 순위 값, nice 값, 스케줄 큐에 대한 포인터 등
6. 프로세스 종료 코드 (정수 0~255)
- 프로세스가 종료할 때 남기는 정수 값. exit() 시스템 호출의 매개변수 값. main() 함수의 return 리턴 값. 부모 프로세스에게 전달
7. 프로세스의 오픈 파일 테이블 : 열어놓은 파일 디스크립터들이 저장되는 배열
8. 메모리 관리 정보
- 페이지 테이블의 물리 메모리 주소 등
9. 프로세스 사이의 통신 정보들
10. 회계 정보
- CPU의 사용 시간, 제한 시간, 프로세스의 총 경과시간 등, 사용료 계산이나 성능 통계에 사용
11. 프로세스 소유자 정보
- 프로세스를 생성한 사용자의 로그인 이름이나 사용자 ID등
* 운영체제마다 프로세스 제어 블록에 저장되는 요소와 프로세스 상태 등이 다름
프로세스 생명 주기와 상태 변이 (State Change) ★
■ 프로세스의 생명 주기
- 프로세스는 탄생에서 종료까지 여러 상태로 바뀌면서 실행
- 상태 정보는 PCB에 기록되고, 상태가 바뀔 때마다 갱신됨
프로세스의 상태
■ New (생성 상태)
- 프로세스가 생성된 상태. 메모리 할당 및 필요한 자원이 적재된 상태
- PCB에 New 상태로 등록. 실행 준비를 마치면 Ready 상태로 바뀜
■ Ready (준비 상태)
- 프로세스가 스케줄링을 기다리는 '준비 상태'
- 프로세스는 준비 큐에서 대기
- 스케줄링되면 Running 상태로 되고 CPU에 의해 실행됨
■ Running (실행 상태)
- 프로세스가 CPU에 의해 현재 실행되고 있는 상태
- CPU의 시간할당량(타임슬라이스)이 지나면 다시 Ready 상태로 바뀌고 준비 큐에 삽입
- 프로세스가 입출력을 시행하면 커널은 프로세스를 Blocked 상태로 만들고 대기 큐에 삽입
■ Blocked / Wait (블록 상태)
- 프로세스가 자원을 요청하거나, 입출력을 요청하고(ex: read() 시스템 호출) 완료를 기다리는 상태
- 입출력이 완료되면 프로세스는 Ready 상태로 바뀌고 준비 큐에 삽입
■ Terminated / Zombie 상태 ★
- 프로세스가 불완전 종료된 상태 (좀비 상태)
- 프로세스가 차지하고 있던 메모리와 할당받았던 자원들을 모두 커널에 의해 반환됨. 커널에 의해 열어 놓은 파일도 닫힘
- 하지만, 프로세스 테이블의 항목과 PCB가 여전히 시스템에서 제거되지 않은 상태
- 프로세스가 남긴 종료 코드 (PCB에 있음)를 부모 프로세스가 읽어가지 않아 완전히 종료되지 않은 상태 - 좀비 상태라고 부름
■ Terminated / Out 상태
- 프로세스가 종료하면서 남긴 종료 코드(PCB에 있음)를 부모 프로세스가 읽어 가서 완전히 종료된 상태
- 프로세스 테이블의 항목과 PCB가 시스템에서 완전히 제거된 상태
프로세스 스케줄링과 컨텍스트 스위칭
■ 프로세스 스케줄링과 스레드 스케줄링
● 프로세스 스케줄링
- 과거 운영체제에서 실행 단위는 프로세스 였음
- Ready 상태의 프로세스 중에 실행 시킬 프로세스 선택
● 오늘날 운영체제는 스레드를 대상으로 스케줄링
- 오늘날 프로세스 스케줄링은 없음
- 오늘날 운영체제에서 실행 단위는 스레드
- Ready 상태의 스레드 중 실행시킬 스레드 선택
■ 프로세스의 역할
- 프로세스는 스레드들에게 공유 자원을 제공하는 컨테이너로 역할이 바뀌었음
3. 프로세스의 계층 구조
프로세스의 부모-자식 관계
■ 프로세스는일반적으로부모-자식 관계
- 윈도우에서 프로세스는 모두 동등 - 계층 관계 아님
- #0 프로세스가시스템부팅 시 실행되는최초의프로세스, 조상프로세스
- 부모 프로세스는여러 개의 자식 프로세스를가질 수 있음
- 모든 프로세스는부모 프로세스를가짐(#0 프로세스제외)
■ 자식 프로세스의생성
- 모든 프로세스는프로세스(부모 )에 의해생성
- 프로세스 생성은 시스템 호출을 통해서만 가능
- fork(), clone() 등의 커널 코드가 자식 프로세스 생성
- 예외 : PID 0, 1, 2 등의 몇몇 조상 프로세스는 시스템 호출이 아닌 수작업(hand-craft)으로 생성
■ 리눅스사례
- #0 프로세스– swapper/idle 프로세스(hand-crafted)
- #1 프로세스– init 프로세스(hand-crafted)
- 부팅 후 생성되는 모든 사용자 프로세스의 조상
- #2 프로세스– kthreadd 프로세스(hand-crafted)
- 커널 모드에서 커널 코드로만 실행되는 모든 커널 프로세스(thread)의 조상
프로세스를 다루는 시스템 호출
memo : 시스템 콜 함수
■ fork()
- 자식 프로세스를 생성하는 시스템 호출
■ exit()
- 현재 프로세스의 종료를 커널에 알리는 시스템 호출
- 현재 프로세스의 종료를 처리하는 커널 코드 실행
■ wait()
- 부모가 자식 프로세스의 종료를 기다리고 확인하는 시스템 호출
부모 프로세스와 자식 프로세스의 실행 관계 ★
좀비 프로세스 - 종료 후 방치된 자식 프로세스
■ 프로세스가 종료할 때
- PCB에 종료코드 (Exit Status) 저장
- PCB에 프로세스 상태를 Terminated라고 표시
- 프로세스에게 할당된 모든 메모리 반환
- PCB와 프로세스 테이블의 항목은 제거되지 않음
■ 부모 프로세스의 의무
- wait() 시스템 호출을 통해 자식 프로세스의 종료 코드를 읽어야함
- 자식이 종료되면 부모에게 SIGCHLD 신호가 전송됨. 부모가 이 신호를 받았을 때 wait() 시스템 호출을 하도록 작성되어 있지 않다면 자식 프로세스는 계속 좀비 상태로 남아 있음
■ 좀비 프로세스 (Zombie Process)
- 종료했지만, 부모가 종료코드를 읽지 않은 상태의 프로세스
- 프로세스 테이블에는 아직 남아 있으므로, 프로세스 목록을 출력할 때(ps 명령으로) 나타남
● 좀비 프로세스 제거 방법
방법 1. 쉘에서 부모 프로세스에게 SIGCHLD 신호 보내기
- $ kill -SIGCHLD 부모프로세스의 PID'
- 부모 프로세스의 SIGCHLD 핸들러가 wait() 호출하여 좀비 자식 제거
- 부모의 SIGCHLD 핸들러가 wait() 호출하지 않으면 좀비는 제거되지 못함
방법 2. 부모 프로세스 강제 종료 (일반적으로 많이 사용함)
- $ kill -9 부모프로세스의 PID'
- 좀비는 init 프로세스의 자식이 되고,
- init이 wait() 호출하여 좀비 프로세스 제거
부모 프로세스에서 자식의 종료를 확인하는 wait() 시스템 호출
고아 프로세스와 입양
■ 고아 프로세스 (Orphan Process)
- 부모가 먼저 종료한 자식 프로세스
■ 부모 프로세스가 종료할 때
● 일반적으로
- 커널 (exit() 시스템 호출 코드)은 자식 프로세스가 있는지 확인
- 커널은 자식 프로세스(고아)를 init 프로세스에게 입양
● 운영체제에 따라, 혹은 쉘의 경우
- 모든 자식 프로세스 강제 종료시키기도 함
■ #0 init 프로세스에 입양되는 과정
여러 종류의 프로세스
■ 백그라운드 프로세스와 포그라운드 프로세스
● 백그라운드 프로세스
- 터미널에서 실행되었지만, 터미널 사용자와의 대화가 없는 채 실행되는 프로세스
- 사용자와 대화없이 실행되는 프로세스
- 사용자 입력을 필요로 하지 않는 프로세스
- idle 상태로 잠을 자거나 디스크에 스왑된 상태의 프로세스
● 포그라운드 프로세스
- 실행되는 동안 터미널 사용자의 입력을 독점하는 프로세스
■ CPU 집중 프로세스 vs. I/O 집중 프로세스
● CPU 집중 프로세스 (CPU intensive process)
- 대부분의 시간을 계산 중심의 일(CPU 작업)을 하느라 보내는 프로세스
- 배열 곱, 인공지능 연산, 이미지 처리 등의 작업
- CPU 속도가 성능 좌우 (CPU bound)
● I/O 집중 프로세스 (I/O intensive process)
- 입출력 작업을 하느라 대부분의 시간을 보내는 프로세스
- 네트워크 전송, 파일 입출력에 집중된 프로세스
- 파일 서버, 웹 서버
- 입출력 장치나 입출력 시스템의 속도가 성능 좌우 (I/O bound)
-> 운영체제의 스케줄링 우선순위 : I/O 집중 프로세스 > CPU 집중 프로세스
- I/O 작업하는 동안 다른 프로세스에게 CPU 할당 가능
4. 프로세스 제어 (fork, exec, wait, exit)
프로세스 생성
■ 컴퓨터 시스템에서 프로세스가 생성되는 5가지 경우
- 시스템 부팅과정에서 필요한 프로세스
- 사용자의 로그인 후 사용자와 대화를 위한 프로세스 생성 (bash등 쉘)
- 새로운 프로세스를 생성하도록 하는 사용자의 명령 (vi a.c)
- 배치 작업 실행 시 (at, batch 명령)
- 사용자 응용프로그램이 시스템 호출로 새 프로세스 생성
■ 프로세스 생성
- 프로세스가 프로세스를 생성
- 시스템 호출을 통해서만 프로세스 생성
- 커널 만이 프로세스를 생성하는 작업 가능
- 리눅스 : fort() 시스템 호출
- Windows : CreateProcess() 등 시스템 호출
프로세스 생성 과정
■ 프로세스의 생성 과정
- 새로운 PID 번호 할당
- PCB 구조체 생성
- 프로세스 테이블에서 새 항목 할당
- 새로 할당된 프로세스 테이블 항목에 PCB 연결
- 새로운 프로세스를 위한 메모리 공간 할당
- 프로세스의 코드, 데이터, 스택, 힙 영역
- 할당받은 메모리 공간에 프로세스의 코드와 데이터 적재
- PCB에 프로세스 정보 기록
- PCB에 프로세스 상태를 Ready로 표시하고, 준비 큐에 넣어서 차후 스케줄되게 함
fork() 시스템 호출로 자식 프로세스 생성
■ fork() 시스템 호출
- 현재 프로세스를 복사하여 자식 프로세스 생성
● int pid = fork();
- 자식 프로세스 생성
- 부모 프로세스의 모든 환경, 메모리, PCB 등을 복사
- 부모와 동일한 모양이지만, 독립된 주소 공간 소유
● 리턴 값
- 부모 프로세스에게는 자식 프로세스의 PID 리턴
- 자식 프로세스에게는 0 리턴
■ fork()의 실행 과정
프로세스 오버레이, exec()
■ 프로세스 오버레이 (Process Overlay)
- 현재 실행중인 프로세스의 주소 공간에 새로운 응용프로그램을 적재하여 실행시키는 기법
- exec 패밀리 시스템 호출
- execlp(), execv(), execvp() 시스템 호출들
- 실행 파일을 적재하여 현재 프로세스의 메모리 공간에 단순히 덮어쓰고, 새로운 프로세스의 생성 과정을 거치지 않음.
- 프로세스의 PID 변경 없음
- 프로세스의 코드, 데이터, 힙, 스택에 새로운 응용프로그램이 적재됨
- fork()에 의해 생성된 자식 프로세스는 생성 후 바로 exec()을 실행하는 경우가 다반사임
■ exec()을 이용해 /bin/ls 응용프로그램을 실행시키는 사례
프로세스 종료와 프로세스 종료 대기
■ 프로세스 종료
- exit() 시스템 호출
- C 프로그램의 main()에서 리턴
- exit() 시스템 호출이 진행됨
■ 종료 코드
- 부모 프로세스에게 전달하는 값
- main() 함수의 리턴 값; return 종료코드;
- exit(종료코드)
■ exit() 시스템 호출로 프로세스 종료 과정
1. 프로세스의 모든 자원 반환
- 코드, 데이터, 스택, 힙 등의 모든 메모리 자원을 반환
- 열어놓은 파일이나 소켓 등 닫음
2. PCB에 프로세스 상태를 Terminate로 변경, PCB에 종료 코드 저장
3. 자식 프로세스들이 있으면 이들을 init 프로세스에게 입양
4. 부모 프로세스에게 SIGCHLD 신호 전송
- 부모가 SIGCHLD 신호 핸들러를 작성하고 여기서 wait() 시스템 호출을 이용해 자식의 종료 코드 읽기 실행
- 혹은 언젠가 부모가 자식의 죽음 처리. 그동안 자식은 좀비 상태에 있음
종료 코드의 범위와 의미
■ 종료 코드 (Exit Code)
- 프로세스가 종료한 상태나 이유를 부모에게 전달하기 위한 것
- POSIX 표준에서 0~255사이의 1바이트 숫자
- 정상 종료는 0
- 1~255 - 개발자가 종료 이유를 임의로 정해 사용
■ 종료 코드 사용 시 유의할 점
- main이나 exit()에서 255 이상의 값을 사용할 때 유의
- -1을 리턴하는 경우 (return -1, 혹은 exit(-1))
- -1 -> 0xff -> 양의 정수로 255. 그러므로 종료 코드로 255가 전달됨
프로세스 종료와 좀비 프로세스 ★
■ 프로세스 종료
● 두 종류
- C언어에서 main() 함수의 종료나 exit()을 호출한 정상 종료
- 다른 프로세스에 의해 강제 종료(kill)
● 프로세스가 종료되면
- 차지하고 있던 메모리와 자원 모두 반환
- PCB는 프로세스 테이블에서 제거되지 않음
- 프로세스 상태 : Terminated
- 부모 프로세스가 wait() 시스템 호출을 통해 죽은 자식이 남긴 종료 코드를 읽게 되면 자식 프로세스의 PCB가 제거
■ 좀비 프로세스
- 종료할 때 리턴한 정보(main() 함수에서 리턴값, 종료 코드)를 부모 프로세스가 읽지 않았을 때, 죽었지만 PCB만 남아 완전히 제거되지 못한 상태