1. 프로세스의 정의
• 프로세스(Process): 운영체제가 제공하는 핵심 개념 중 하나로, 실행 중인 프로그램을 의미한다. 프로그램은 디스크에 저장된 명령어와 정적 데이터의 집합이며, 운영체제는 이를 메모리에 로드하여 실행 가능하게 만든다.
• 실행 중인 프로그램: 사용자들은 웹 브라우저, 메일 프로그램, 게임, 음악 플레이어 등 여러 프로그램을 동시에 실행시킴으로써 시스템을 효율적으로 사용할 수 있다. 운영체제는 이러한 다중 프로세스를 관리하여 사용자에게 원활한 경험을 제공한다.
2. 핵심 질문: CPU가 여러 개 존재한다는 환상을 어떻게 제공하는가?
• 가상화(Virtualization): 운영체제는 실제로는 하나 또는 소수의 물리적 CPU를 여러 개의 가상 CPU로 분할하여 다수의 프로세스가 동시에 실행되는 것처럼 보이게 한다.
• 시분할(Time Sharing): 운영체제는 프로세스 간에 CPU 시간을 빠르게 전환하면서 각 프로세스가 CPU를 독점적으로 사용하는 것처럼 느껴지도록 한다. 이를 통해 다중 프로세스가 효율적으로 CPU를 공유할 수 있게 한다.
• 문맥 교환(Context Switch): 운영체제가 프로세스 간에 전환할 때 현재 실행 중인 프로세스의 상태(레지스터 등)를 저장하고, 다음 실행할 프로세스의 상태를 복원하는 작업.
3. 프로세스 API
• 생성(Create): 새로운 프로세스를 생성하는 인터페이스를 제공. 예를 들어, 쉘에서 명령어를 입력하거나 프로그램 아이콘을 더블 클릭하면 운영체제가 새로운 프로세스를 생성한다.
• 제거(Destroy): 실행 중인 프로세스를 종료시키는 인터페이스를 제공. 사용자가 필요에 따라 프로세스를 중단시킬 수 있도록 한다.
• 대기(Wait): 특정 프로세스가 종료될 때까지 기다리는 인터페이스를 제공. 부모 프로세스가 자식 프로세스의 종료를 기다리는 경우 등이 이에 해당.
• 제어(Miscellaneous Control): 프로세스를 일시정지하거나 재개하는 등의 다양한 제어 기능을 제공.
• 상태(Status): 프로세스의 현재 상태(실행 중, 준비, 대기 등)를 조회할 수 있는 인터페이스를 제공.
4. 프로세스 생성의 세부 과정
• 프로그램 로드: 운영체제는 디스크에 저장된 프로그램의 코드와 정적 데이터를 메모리에 로드한다. 현대 운영체제는 필요한 시점에 필요한 부분만 로드하는 지연 로딩(lazy loading)을 사용하기도 한다.
• 메모리 할당: 스택(Stack)과 힙(Heap) 메모리를 할당하여 함수 호출과 동적 메모리 할당을 지원한다.
• 입출력 초기화: 프로세스가 표준 입력(STDIN), 표준 출력(STDOUT), 표준 에러(STDERR)와 같은 기본 입출력 스트림을 사용할 수 있도록 초기화한다.
• 실행 시작: 모든 초기화가 완료되면, 운영체제는 프로세스의 시작 지점(main 함수)으로 제어를 넘겨 프로그램 실행을 시작한다.
5. 프로세스 상태
• 실행(Running): 프로세스가 CPU에서 실행 중인 상태.
• 준비(Ready): 프로세스가 실행을 기다리고 있는 상태로, CPU를 사용할 준비가 되어 있음.
• 대기(Blocked): 프로세스가 입출력과 같은 외부 이벤트를 기다리느라 실행을 중단한 상태.
• 좀비(Zombie): 프로세스가 실행을 마쳤지만, 부모 프로세스가 종료 상태를 수집하지 않아 메모리에 남아 있는 상태.
• 프로세스 상태 전이: 프로세스는 준비 상태와 실행 상태 사이를 오가며, 입출력 요청 시 대기 상태로 전환된다. 이벤트가 발생하면 다시 준비 상태로 돌아와 실행될 수 있다.
6. 자료 구조: 프로세스 리스트와 PCB
• 프로세스 리스트(Process List): 현재 시스템에서 실행 중인 모든 프로세스를 관리하기 위한 자료 구조.
• 프로세스 제어 블럭(Process Control Block, PCB): 각 프로세스에 대한 정보를 저장하는 구조체로, 프로세스의 상태, 메모리 정보, CPU 레지스터 상태, 파일 디스크립터 등 다양한 정보를 포함한다.
• 예시 (xv6 Proc 구조체):
struct proc {
char *mem; // 프로세스의 메모리 공간
uint sz; // 프로세스의 메모리 크기
char *kstack; // 프로세스의 커널 스택
enum proc_state state; // 프로세스 상태
int pid; // 프로세스 ID
struct proc *parent; // 부모 프로세스
void *chan; // 대기 채널
int killed; // 종료 여부
struct file *ofile[NOFILE]; // 열린 파일 리스트
struct inode *cwd; // 현재 작업 디렉토리
struct context context; // 프로세스의 레지스터 상태
struct trapframe *tf; // 트랩 프레임
};
• 설명:
• mem: 프로세스의 메모리 공간을 가리킴.
• sz: 프로세스 메모리의 크기.
• kstack: 커널 모드에서 사용하는 스택.
• state: 프로세스의 현재 상태(UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE).
• pid: 프로세스의 고유 식별자.
• parent: 부모 프로세스를 가리킴.
• chan: 프로세스가 대기 중인 이벤트 채널.
• killed: 프로세스가 종료 요청을 받았는지 여부.
• ofile: 열린 파일 리스트.
• cwd: 현재 작업 디렉토리.
• context: 문맥 교환 시 저장되는 레지스터 상태.
• tf: 트랩 프레임으로, 시스템 콜이나 인터럽트 시점의 상태를 저장.
7. 요약
• 프로세스 관리의 중요성: 운영체제는 다중 프로세스를 효율적으로 관리하여 사용자에게 원활한 컴퓨팅 환경을 제공한다.
• 가상화와 시분할: 운영체제는 가상화를 통해 물리적 자원을 추상화하고, 시분할 기법을 통해 다중 프로세스가 동시에 실행되는 것처럼 보이게 한다.
• 프로세스 상태와 전이: 프로세스는 실행, 준비, 대기 등의 상태를 가지며, 운영체제는 이러한 상태 전이를 관리하여 자원을 효율적으로 분배한다.
• 자료 구조의 역할: PCB와 프로세스 리스트와 같은 자료 구조는 운영체제가 프로세스를 추적하고 관리하는 데 필수적이다.