7.1 프로세스의 개념
- 프로세스는 실행 중인 프로그램으로, 특정 시점에서 프로세스는 접근하거나 영향을 미친 자원의 목록으로 간단히 표현할 수 있다.
- 프로세스를 구성하는 주요 하드웨어 상태:
- 메모리: 명령어와 데이터가 저장되는 장소.
- 레지스터: 실행 중 명령어들이 읽고 쓰는 하드웨어 상태.
- 특수 레지스터:
- 프로그램 카운터(Program Counter, PC): 현재 실행 중인 명령어의 위치를 나타냄.
- 스택 포인터(Stack Pointer) & 프레임 포인터(Frame Pointer): 함수 호출 시 변수와 리턴 주소를 관리.
- 영구 저장장치: 프로세스는 파일 목록을 통해 입출력 정보에 접근한다.
7.2 프로세스 API
운영체제가 제공하는 프로세스 관리 API의 기본 기능은 다음과 같습니다:
-
생성 (Create):
- 새로운 프로세스를 생성하는 기능.
- 쉘 명령어 실행, 아이콘 더블 클릭 등을 통해 새로운 프로세스가 생성됨.
-
제거 (Destroy):
- 프로세스를 종료하거나 강제로 제거하는 기능.
- 프로세스가 스스로 종료하지 않을 경우 사용자가 종료 가능.
-
대기 (Wait):
- 특정 프로세스가 실행을 멈추기를 기다리는 기능.
- 동기화 작업을 지원하기 위해 여러 대기 인터페이스 제공.
-
제어 (Miscellaneous Control):
- 프로세스 일시 정지 및 재개 등의 다양한 제어 기능.
-
상태 (Status):
- 프로세스 상태 정보를 확인하는 기능.
- 실행 시간, 현재 상태 등의 정보를 포함.
7.3 프로세스 생성: 좀 더 자세하게
프로세스 생성은 프로그램이 실행 가능한 상태로 변환되는 과정으로, 운영체제가 수행하는 작업은 다음과 같습니다:
-
코드와 정적 데이터의 메모리 탑재:
- 프로그램 코드를 디스크에서 메모리로 읽어오는 작업.
- 현대 운영체제는 필요할 때만 데이터를 메모리에 탑재하는 지연 탑재(lazy loading) 방식을 사용.
-
실행 시간 스택(run-time stack) 할당:
- 지역 변수, 함수 인자, 리턴 주소 등을 저장하기 위해 스택 메모리 초기화.
main() 함수의 인자(argc, argv)를 이용해 초기화.
-
힙(heap) 메모리 영역 할당:
- 동적 메모리 할당을 위한 메모리 공간.
malloc()과 free() API를 통해 힙 메모리를 관리.
-
입출력 초기화:
- 기본 파일 디스크립터 생성:
STDIN, STDOUT, STDERR.
- 터미널 입력/출력 처리를 지원.
-
프로그램 실행 준비:
- 모든 초기화 작업 후, 프로그램의 시작 지점(entry point, 일반적으로
main())으로 분기.
- CPU를 새 프로세스에 넘겨 프로그램 실행 시작.
7.4 프로세스 상태
프로세스는 실행 중에 다음 세 가지 상태 중 하나에 있을 수 있습니다:
-
실행(Running):
- 프로세스가 CPU에서 실행 중이며 명령어를 처리하고 있는 상태.
-
준비(Ready):
- 프로세스가 실행할 준비가 되었으나, CPU를 다른 프로세스가 사용 중이라 대기하는 상태.
-
대기(Blocked):
- 프로세스가 I/O 요청과 같은 이벤트를 기다리는 동안 실행이 중단된 상태.
- 예: 디스크 읽기/쓰기 요청 완료를 기다리는 경우.
상태 전이
- 준비 ↔ 실행: 스케줄러의 결정에 따라 준비 상태에서 실행 상태로 전이되거나, 실행 상태에서 준비 상태로 전환.
- 실행 → 대기: I/O 요청 등으로 인해 실행이 중단되면 대기 상태로 전환.
- 대기 → 준비: I/O 요청 완료 등 특정 이벤트 발생 시 다시 준비 상태로 전환.
7.5 자료 구조
운영체제는 프로세스 관리를 위해 다양한 자료 구조를 사용합니다:
-
프로세스 리스트(Process List):
- 시스템에서 실행 중인 모든 프로세스를 관리하는 자료 구조.
- 준비 상태, 실행 상태, 대기 상태 프로세스를 각각 추적.
-
프로세스 제어 블록(Process Control Block, PCB):
- 각 프로세스의 상태 정보를 저장하는 자료 구조.
- 포함되는 정보:
- 프로세스 ID (PID)
- 프로세스 상태 (Running, Ready, Blocked)
- CPU 레지스터 값
- 메모리 관리 정보 (페이지 테이블 등)
- 열린 파일 목록
- 스케줄링 정보 (우선순위 등)
-
문맥 교환(Context Switch):
- 실행 중이던 프로세스의 레지스터 상태를 PCB에 저장하고, 다른 프로세스의 레지스터 상태를 복원하여 실행을 전환하는 과정.
- 효율적인 CPU 자원 활용을 위한 핵심 기법.
추가 프로세스 상태
기본적인 실행(Running), 준비(Ready), 대기(Blocked) 상태 외에도, 프로세스의 라이프사이클에서 다음과 같은 상태가 존재할 수 있습니다:
-
초기 상태 (Initial):
- 프로세스가 생성 중인 상태.
- 프로세스가 메모리에 로드되고 초기화 작업이 완료되기 전까지 유지.
-
최종 상태 (Final, Zombie):
- 프로세스가 종료되었지만, 아직 메모리에 남아 있는 상태.
- 좀비 상태(Zombie)라고도 불리며, 주로 Unix 기반 시스템에서 사용.
좀비 상태의 특징
-
역할:
- 부모 프로세스가 자식 프로세스의 종료 상태를 확인할 수 있도록 정보를 보존.
- 자식 프로세스의 종료 코드를 통해 성공 여부를 알림:
- 0: 성공적으로 종료.
- 0이 아닌 값: 오류 발생.
-
상태 관리:
- 부모 프로세스는
wait()와 같은 시스템 콜을 통해 자식 프로세스의 종료를 대기.
wait() 호출 시 운영체제가 종료된 프로세스와 관련된 자원을 정리하고 좀비 상태를 제거.
-
제거 방법:
- 일반적으로 부모 프로세스가 자식 프로세스의 종료를 처리하면 좀비 상태가 자동으로 해제됨.
- 부모 프로세스가 종료되지 않은 좀비 프로세스를 방치할 경우, init 프로세스(PID 1)가 자식을 회수(reap)하며 좀비를 제거.
좀비 상태 관리의 중요성
- 좀비 상태는 자원을 거의 사용하지 않지만, 너무 많은 좀비 프로세스가 남아 있으면 프로세스 테이블이 가득 차는 문제를 야기할 수 있음.
- 예방 조치:
- 부모 프로세스에서 주기적으로
wait() 호출.
- 필요할 경우 부모 프로세스를 종료하거나 SIGCHLD 신호를 활용.
부모 프로세스로부터 detach된 프로세스는 좀비 프로세스와는 다른 상태입니다. 이 경우는 오히려 고아 프로세스(Orphan Process)로 분류됩니다. 이를 정확히 구분해보겠습니다.
좀비 프로세스(Zombie Process) vs. 고아 프로세스(Orphan Process)
1. 좀비 프로세스
- 정의:
- 프로세스가 종료되었지만, 부모 프로세스가 자식의 종료 상태를 회수하지 않아 PCB가 남아 있는 상태.
- 특징:
- 더 이상 실행되지 않으며, CPU를 사용하지 않음.
- 프로세스의 종료 코드 및 정보를 PCB에 저장하고 부모가
wait()을 호출하기를 기다림.
- 해결:
- 부모 프로세스가
wait() 호출.
- 부모 프로세스가 종료되면, 고아 프로세스처럼 init 프로세스(PID 1)가 자식을 회수하여 좀비 상태 해제.
2. 고아 프로세스
- 정의:
- 부모 프로세스가 먼저 종료되어 부모가 없는 상태에서 실행을 계속하는 프로세스.
- 특징:
- 여전히 실행 중이며, CPU와 메모리 등 자원을 사용함.
- Linux 및 Unix 시스템에서는 init 프로세스(PID 1)가 고아 프로세스를 자동으로 인계받아 관리.
- 해결:
- 고아 프로세스 자체는 시스템적으로 문제가 되지 않음.
init 프로세스가 정상적으로 관리하기 때문.
- 만약 고아 프로세스가 자원을 과도하게 소비한다면, 수동으로 종료(
kill -9)해야 할 수 있음.
Detach된 프로세스
1. Detach란?
- Detach된 프로세스는 부모-자식 관계에서 독립적으로 동작하도록 설정된 프로세스입니다.
- 부모 프로세스는 자식 프로세스를
wait()로 관리하지 않으며, 자식은 자신의 종료 상태를 부모에게 알리지 않습니다.
2. Detach와 좀비 프로세스의 차이
- Detach된 프로세스는
daemon 프로세스처럼 독립적으로 실행되도록 설계된 프로세스입니다.
- 부모가 종료되거나 관계를 명시적으로 끊었기 때문에 좀비 상태로 남지 않습니다.
- 좀비 상태는 부모가 종료 상태를 회수하지 않아 PCB가 잔존하는 경우에만 발생합니다.