프로그램은 디스크에 저장된 명령어들의 집합(.exe)이고, 이를 메모리에 적재하여 CPU가 실행 가능한 상태로 만든 것이 프로세스다. 프로세스는 실행에 필요한 자원(메모리 구조, PC카운터, 레지스터)과 함께 운영체제를 통해 관리된다.
프로세스는 역할 또는 병행수행 방법 등에 따라 구분할 수 있다.
프로세스는 역할에 따라 사용자 프로세스와 시스템(커널) 프로세스로 구분된다.
프로세스는 병행수행 방법에 따라 독립 프로세스와 협력 프로세스로 구분할 수 있다.
운영체제는 프로세스에서 생성, 종료, 제거, 우선순위 변경, 컨텍스트 스위칭 등 다양한 작업을 수행할 수 있는데 이는 먼저 프로세스의 구조를 알아야 이해할 수 있다.
프로세스는 실행 중에 프로세스 생성 시스템 콜을 이용해 새로운 프로세스를 생성할 수 있다. 이로 인해 프로세스는 부모-자식 관계를 유지하여 계층 구조를 이룰 수 있다. 당연하게도 먼저 생성된 프로세스가 부모 프로세스이고, 어떤 프로세스에 의해 생성된 프로세스를 자식 프로세스라고 한다.
운영체제 또는 응용 프로그램에서 요청을 받아 프로세스를 생성하면 운영체제는 해당하는 PCB를 만들고 주소 공간을 할당한다.
응용 프로그램에서 fork(), exec() 명령어를 통해 자식 프로세스를 생성할 수 있는데 두 경우로 나뉘어진다.
fork() 명령어 호출 후 exec() 명령어를 연달아 호출해 자식 프로세스의 주소 공간을 별도 프로그램 주소 공간으로 덮어 쓰는 경우운영체제가 프로세스를 생성하면 PCB를 만들고 할당하는 논리적(가상) 메모리 공간을 프로세스의 주소 공간이라고 한다.
이 공간은 프로세스가 실행되면서 사용하는 코드, 데이터, 스택, 힙 영역 등으로 구성되어 있다.
이러한 공간 분할 덕분에 공간별로 목적과 생명주기가 다른 데이터들을 구분해서 저장해 효율적으로 관리할 수 있다. 예를들어 지역 변수는 지역 변수가 위치하는 함수가 호출되어 종료되는 그 사이에서만 사용되는 일시적인 생명주기를 가지므로, 스택 영역에 저장해 효율적으로 관리한다.

malloc() 또는 free() 명령어를 통해 동적 할당/해제 한다. 힙은 인접한 스택의 방향으로 메모리 공간이 늘어나는데 마찬가지로 스택 포인터와 만나게 되면 메모리가 소진된 상태가 되어 운영체제에 의해 프로세스가 강제 종료된다.스택과 힙에 대해서
- 스택과 힙은 무한정 늘어날 수 있을까?
- 프로세스의 주소 공간은 가상 메모리지만 연결된 물리 공간이 있기에 결국 제한된 크기를 가진다. 스택은 프로세스 시작 시 운영체제에 의해서 최대 크기가 정해지지만, 힙은 동적 영역이기 때문에 프로세스의 여유 공간만큼 늘어날 수 있다.
- 스택과 힙 중 어느 공간이 더 빠를까?
- 스택은 자료구조 스택과 같은 개념으로 LIFO 구조를 갖고 메모리를 연속적으로 사용해 캐시 적중률이 높다. 하지만 힙은 자료구조 힙과 관련 없는 동적 영역으로 메모리를 비연속적으로 사용해 캐시 적중률이 낮고 동적 탐색과 병합이 필요하다. 이러한 차이로 인해 스택 영역이 힙 영역에 비해 훨씬 빠르다.
- 스택은 자료구조 스택과 같기 때문에 남은 메모리 공간이 없으면 에러가 발생하는데 이 에러가 바로 스택 오버플로우다.
프로세스 제어 블록(PCB, Process Control Block)은 운영체제가 프로세스를 제어할 때 필요한 프로세스의 상태 정보가 담겨있는 자료구조다. PCB의 생명주기는 프로세스의 생명주기와 거의 일치하는데, 프로세스가 생성되면 메모리에 PCB가 생성되고 프로세스가 종료될 때 PCB가 삭제된다.

운영체제가 모든 모듈의 정보를 읽고 수정할 수 있다.
프로세스가 마지막 명령을 실행하면 종료하여 운영체제에 프로세스의 삭제를 요청한다. 일괄 처리 환경에서는 인터럽트를 발생시키거나 시스템 콜로 중단 명령을 전달해 프로세스를 종료한다. 또한, abort() 명령어를 통해 프로세스를 종료할 수 있다. 이 명령어는 종료할 프로세스의 부모 프로세스만 호출하는데 이는 부모 프로세스만 자식 프로세스를 종료시킬 수 있기 때문이다.
연속 종료
부모 프로세스가 종료되면 보통 자식 프로세스도 필요하지 않기 때문에 운영체제가 자식 프로세스도 종료시키는데 이를 연속 종료라고 한다.
그리고 자식 프로세스는 자신의 식별자(PID)를 부모 프로세스에 전달한다. 이는 프로세스가 종료되면 자원이 해제되지만 운영체제는 프로세스 테이블에 종료 상태를 남겨두어 부모 프로세스가 이를 파악할 수 있게 하기 때문이다. 부모 프로세스는 wait() 시스템 콜을 통해 자식 프로세스의 종료 여부를 파악한 후 프로세스 테이블에 제거를 요청해 비로소 프로세스가 제거된다. 하지만 이때 문제 되는 상황이 있다.
wait() 시스템 콜을 통해 프로세스 테이블을 확인해 종료된 자식 프로세스를 알게 된다면 정상 흐름wait()을 호출하지 않아 프로세스 테이블에 남아있게 되고 이를 '좀비 프로세스'라고 한다. 좀비 프로세스와 고아 프로세스 모두 시스템 종료까지 운영체제가 해당 프로세스를 추적해야 해서 성능 저하의 원인이 된다. 이에 대한 해결 방법은 다음과 같다.
wait() 시스템 콜 호출을 통해 예방할 수 있다. 하지만 시스템 콜은 커널의 개입으로 인해 오버헤드가 발생한다는 단점이 생긴다.wait() 시스템 콜을 호출해 고아 프로세스를 종료·제거한다.프로세스의 상태는 프로세스의 현재 활동으로 정의한다. 프로세스의 상태는 크게 실행 상태와 비실행 상태로 구분할 수 있으며, 운영체제가 프로세스를 생성하면서 비실행 상태에서 준비 상태로 초기화되며 실행을 기다린다.
CPU를 점유하고 있던 실행 상태의 프로세스가 종료되거나 인터럽트를 당하면 운영체제는 준비 상태에 있는 프로세스를 하나 선택해 실행 상태로 전환하고 이 전환을 디스패치(dispatch)라고 한다.
실행 중인 프로세스가 I/O 요청 등으로 이벤트가 발생한 경우, 해당 프로세스는 대기 상태로 전이되고 인터럽트가 발생하면 다시 준비 상태로 전환되며 실행을 기다린다.

프로세스 스케줄러는 PCB에 있는 우선순위를 이용해 준비 큐의 프로세스를 처리한다. 준비 큐의 프로세스는 프로세서 중심 프로세스와 입출력 중심 프로세스로 구분할 수 있다.
인터럽트나 시스템 콜 등으로 실행 중인 프로세스의 제어를 다른 프로세스에게 넘겨 실행 상태가 되도록 하는 것을 컨텍스트 스위칭이라고 한다. 이는 CPU의 Multi Tasking 작업을 위해 프로세스 간의 전환 중 일어나는 과정 중 하나로, 실행 중이던 프로세스의 현재까지의 상태 정보를 PCB에 저장하고 다시 실행 상태가 되었을 때 PCB를 통해 이전 상태 정보를 불러와 연속적으로 작업이 진행되도록 보장하는 중요한 과정이다.
프로세스는 독립적인 메모리 공간을 갖고 있어 컨텍스트 스위칭 시 주소 공간을 전체 변경해야 되고 PCB와 커널 스택도 함께 교체해야 하므로 오버헤드가 크게 발생한다. 그래서 효율적인 스케줄링 알고리즘을 통해 컨텍스트 스위칭을 최소하하여 성능을 향상시키는 것이 중요하다.
프로세스 메모리 구조
프로세스의 메모리 구조는 유저 공간과 커널 공간으로 나누어져 있다. 두 공간을 나눈 이유는 운영체제의
dual mode와 같은 이유로 시스템 안전을 보장하기 위해서 나누어져 있다.컨텍스트 스위칭이 발생할 때 프로세스의 현재까지의 상태 정보를 커널 스택과 PCB에 저장한다. 커널 스택에는 레지스터 값, 프로그램 카운터(PC) 등 중요한 정보를 저장하고, PCB에는 프로세스의 상태 정보를 저장한다. 이때 커널 스택과 PCB에 모두 구조체 기반으로 저장된다.
추가로 컨텍스트 스위칭은 프로세스뿐 아니라 스레드 간에서도 각각 일어나며, 주의할 점으로 인터럽트가 발생했다고 무조건 컨텍스트 스위칭이 일어나지 않는다. 왜냐하면 인터럽트가 발생해도 Non-Blocking 설정 등으로 인해 해당 프로세스가 여전히 CPU 점유를 하고 있을 수 있기 때문이다.
프로세스 상태 중 '중단 상태'를 추가해서 운영체제가 메모리를 더 유연하게 관리할 수 있도록 할 수 있는데, 이를 스와핑이라고 한다.

학습하며 정리한 글이기 때문에 혼용된 표현 또는 잘못된 내용이 있을 수 있습니다.
만약, 발견하신 경우 댓글을 통해 알려주신다면 진심으로 감사드립니다.