Part 01. 프로세스와 스레드는 어떤 관계일까?
프로세스, 스레드
프로세스
프로세스라는 용어는 1960년대 멀틱스 시스템 설계자들이 작업(job)이라는 말과 함께 처음 사용했으며, 이후 다양하게 정의되어 왔다. 가장 널리 쓰이는 정의는 실행 중인 프로그램이다.
디스크에 저장되어 있던 실행 가능한 프로그램이 메모리에 적재되어 운영체제의 제어를 받는 상태를 프로세스라 한다. 이는 해당 프로세스가 사용하는 메모리 영역, 즉 자신만의 주소 공간이 존재한다는 뜻이기도 하다. 프로세스는 프로세서 점유 시간과 메모리, 파일, 입출력 장치 같은 자원이 필요하며, 이 자원들은 프로세스가 생성되고 실행될 때 할당된다. 또한 현재 활동 상태를 나타내는 프로그램 카운터와, 프로세서의 현재 활동을 나타내는 레지스터 내용도 함께 포함한다.
프로그램과 달리 프로세스는 메모리에 주소 공간을 가진 능동적인 개체다. 이 주소 공간은 몇 가지 영역으로 나뉜다. **실행 스택(Stack)**은 호출된 프로시저(함수)의 복귀 주소와 지역 변수 같은 일시적인 데이터를 저장하고, **실행 힙(Heap)**은 텍스트(코드) 영역과 별도로 유지되는 자유 영역이며, 데이터(정적 변수) 영역은 프로세스 실행 중 동적으로 할당받는 영역으로 전역 변수나 정적 변수를 저장하고, 텍스트(코드) 영역은 프로세서가 실행할 코드를 저장한다.
메모리에는 동일한 사본을 여러 사용자가 공유할 수 있도록 작성된 프로그램이나 루틴이 올라간다. 실행 중 사용할 데이터를 담는 실행 스택과, 공통 데이터를 담는 데이터 영역을 함께 가진다. 프로세서가 같은 프로그램을 사용하더라도 텍스트(코드) 영역은 동일하지만 각자 별도의 스택에 서로 다른 데이터를 갖기 때문에, 이들은 서로 별개의 프로세스로 인식된다(데이터베이스, 문서 편집기 등이 그 예다). 프로세스는 사용자 관점에서는 코드, 데이터, 스택이라는 세그먼트로 이루어진 가상 주소 공간을 가진 상태이고, 시스템 관점에서는 실행 중인 프로그램 자체를 의미한다. 처리 상태에 있는 데이터 구조로도 표현할 수 있다.
프로세스는 실행 유형에 따라 세 가지로 구분한다. 운영체제 프로세스(커널 프로세스, 시스템 프로세스라고도 한다), 사용자 프로세스(사용자 코드를 수행), 병행 프로세스(다시 독립 프로세스와 협동 프로세스로 나뉜다)다.
프로세스는 실행되는 동안 상태가 계속 바뀌므로, 운영체제는 프로세스 제어를 위해 그 상태를 항상 점검해야 한다. 실행 상태는 명령어가 실제로 실행되는, 즉 프로세스가 프로세서를 점유한 상태다. 대기(보류) 상태는 프로세스가 입출력 종료 같은 외부 이벤트가 일어나기를 기다리는 상태다. 준비 상태는 프로세스가 프로세서를 할당받기 위해 기다리는 상태다. 대부분의 프로세스는 준비 또는 대기 상태에 머물러 있고, 어느 한 순간에는 오직 하나의 프로세스만 실행 상태가 된다. 시스템의 모든 프로세스는 종료되어 시스템을 떠날 때까지 준비, 실행, 대기 상태를 오가며, 운영체제는 프로세서 스케줄러로 이 상태 변화를 관리한다.
작업 스케줄러
작업 스케줄러는 스풀러가 디스크에 저장해둔 작업들 가운데 실행할 작업을 골라 준비 리스트에 넣는 역할을 한다. 이 과정에서 다중 프로그래밍의 정도가 결정되므로, 작업 스케줄러는 핵심적인 역할을 맡는다. 선정된 작업은 프로세스가 생성되어 종료되기까지 상태 변화를 거친다. 실행 중인 프로세스가 자발적으로 프로세서를 반환하기 전 할당된 시간이 다 지나면 준비 상태로 바뀌고, 실행 도중 입출력 명령이 발생하면 대기 상태로 바뀐다. 대기하던 프로세스는 보류의 원인이 사라지면 준비 상태로 돌아오고, 준비 상태의 프로세스는 디스패치가 프로세서를 할당하면 다시 실행 상태가 된다.
- 준비 → 실행: 준비 리스트 맨 앞의 프로세스가 프로세서를 배당받아 실행을 시작하는 것을 디스패치라 한다. 이때 특정 프로세스가 프로세서를 독점하지 못하도록 사용 시간을 제한하는 시간할당이 적용된다.
- 실행 → 준비: 프로세스가 프로세서를 점유한 상태를 실행 상태라 하며, 운영체제는 특정 프로세스의 독점을 막기 위해 인터럽트 클록을 둔다.
- 실행 → 대기(보류): 실행 중인 프로세스가 지정된 시간이 지나기 전에 입출력 연산이나 새로운 자원 요청 같은 이유가 생기면, 스스로 프로세서를 양도하고 대기 상태로 바뀐다.
- 대기 → 준비: 입출력 작업이 끝나는 등 대기의 원인이 사라지면 깨움이 일어나며, 이는 프로세스의 마지막 상태 변화다.
프로세스의 교환
프로세스의 교환은 인터럽트, 트랩, 시스템 호출로 나타난다. 실행 중인 프로세스가 인터럽트되어 운영체제가 다른 프로세스를 실행 상태로 바꾸고 제어를 넘길 때는, 원래 프로세서의 레지스터 내용을 저장해야 한다. 프로세스는 현재 사용 중인 자원 정보를 갖고 있으며, 실행되는 과정에서 여러 프로세스를 파생시킬 수 있는 능동적인 개체다. 때로는 자원을 할당받은 상태의 프로그램 그 자체를 의미하기도 한다.
프로세스 관리
실행 중인 프로세스는 시스템 호출을 통해 새로운 프로세스를 생성할 수 있다. 프로세스는 생성된 순서를 유지하며 부모-자식 관계를 이루는 계층 구조로 만들어진다. 프로세스를 생성하는 쪽을 부모 프로세스, 새로 생성되는 쪽을 자식 프로세스라 부른다.
유닉스 시스템은 일반적으로 이런 계층 구조를 갖는다. 부팅 시 식별자가 0인 첫 번째 프로세스 swapper가 생성되고, 이어서 init(PID=1)과 page daemon(PID=2)이 생성된다. swapper(PID=0)와 page daemon(PID=2)은 운영체제 프로세스로, 운영체제 모드에서만 실행된다. 그 밖의 모든 사용자 프로세스는 FORK 명령을 통해 계층적으로 init의 자식 프로세스가 된다.
프로세스는 운영체제 자신이나 다른 사용자의 응용 프로그램 요청에 의해 생성된다. 운영체제가 새 프로세스를 만들려면, 프로세스 관리를 위한 프로세서 제어 블록(PCB)을 만들고 그 프로세스에 주소 공간을 할당해야 한다.
프로세스가 작업을 수행하려면 자원이 필요하다. 자식 프로세스를 생성할 때는 운영체제로부터 직접 자원을 받거나, 부모 프로세스가 가진 자원의 일부를 사용할 수 있다. 부모 프로세스는 자식 프로세스에 자원을 나눠주거나 함께 공유할 수 있으며, 프로세스 생성 시 얻는 물리적·논리적 자원 외에 약간의 초기화 데이터가 부모에서 자식으로 전달되기도 한다.
새 프로세스가 생성될 때 실행 방식은 두 갈래로 나뉜다. 부모와 자식 프로세스가 동시에 실행되거나, 자식 프로세스들이 모두 종료될 때까지 부모가 기다리는 것이다. 또한 자식 프로세스는 부모의 주소 공간을 그대로 복사하거나, 별도의 프로그램을 새로 적재할 수 있다.
프로세스는 명령 실행을 마치면 종료되며 운영체제에 자신을 삭제해달라고 요청한다. 일괄 처리 환경에서는 작업 종료를 알리는 인터럽트가 발생하거나 시스템 호출로 중지 명령이 전달되어 프로세스가 완료되고, 대화형 환경에서는 사용자가 로그아웃하거나 터미널을 닫을 때, 혹은 오류가 발생했을 때 프로세스가 종료된다. ABORT 시스템 호출로도 프로세스를 종료할 수 있다.
부모 프로세스는 자식 프로세스가 할당된 자원을 초과해 사용하거나, 더 이상 할당할 작업이 없을 때 자식 프로세스를 종료시킬 수 있다.
연속 종료는 부모 프로세스가 종료될 때 자식 프로세스의 존재를 허용하지 않아 함께 종료시키는 현상으로, 운영체제가 수행한다. 유닉스에서는 exit 명령으로 프로세스를 종료하고, 부모 프로세스는 wait 명령으로 자식 프로세스의 종료를 기다린다.
프로세스가 종료되는 경우는 다양하다. 운영체제 서비스를 호출해 정상적으로 종료되는 경우, 명시된 전체 시간을 초과해 실행되거나 어떤 이벤트를 기다리다 시간을 초과하는 경우, 파일 검색에 실패하거나 입출력이 명시된 횟수를 넘겨 실패하는 경우, 산술 오류·보호 오류·데이터 오류가 발생하는 경우, 메모리 부족이나 접근 위반이 발생하는 경우 등이다.
프로세스를 파괴하면 그 프로세스에 속한 자원은 시스템에 반납되고, 해당 프로세스는 시스템 리스트나 테이블에서 사라지며 프로세스 제어 블록도 회수된다. 프로그램 자체는 디스크에 그대로 남는다.
프로세스 중단(일시정지)
프로세스 중단은 프로세서의 동작 시간과 입출력 동작 시간의 차이 때문에 시스템이 유휴 상태로 남는 문제를 해결하는 방법이다. 오래 중단될 경우 그 프로세스에 할당된 자원을 다시 풀어줘야 하는데, 어떤 자원을 풀어줄지는 자원의 성질에 따라 다르다. 다중 프로그래밍에서는 중단이 자원 부족(대기) 상태를 의미하기도 한다.
운영체제는 두 가지 방법으로 프로세스를 실행할 수 있다. 새로운 프로세스를 생성해 실행하거나, 이미 실행 중이던 프로세스를 중단시켰다가 다시 실행하는 것이다. 프로세스 재시작은 중단 원인이 사라져 프로세스가 다시 실행되는 것으로, 중단되었던 지점부터 이어서 시작한다.
프로세스 중단과 재시작은 몇 가지 상황에서 일어난다. 시스템에 장애가 생기면 실행 중이던 프로세스를 잠시 중단했다가, 시스템이 기능을 회복하면 다시 시작할 수 있다. 어떤 프로세스의 동작이 의심스러울 때 사용자가 직접 중단시켜 확인한 뒤 재시작하거나 정지할 수도 있고, 처리할 일이 많아 시스템 부담이 클 때는 일부 프로세스를 중단시켰다가 시스템이 정상으로 돌아오면 재시작할 수도 있다.
중단은 프로세스 자신이나 다른 프로세스에 의해 일어날 수 있다. 단일 처리 시스템에서는 프로세스 자신만 스스로를 중단시킬 수 있지만, 다중 처리 시스템에서는 다른 프로세서도 중단시킬 수 있다. 다만 다른 프로세서가 재시작시켜주기 전까지는 다시 실행될 수 없다.
프로세스 제어 블록의 우선순위 값은 상황에 따라 바뀔 수 있다. 준비 리스트의 프로세스는 프로세서 중심 프로세스(낮은 우선순위)와 입출력 중심 프로세스(높은 우선순위)로 나뉘는데, 입출력 중심 프로세스는 자주 짧게 사용하게 하고 프로세서 중심 프로세스는 사용 횟수는 적지만 한 번에 오래 사용하게 함으로써 전체적인 균형을 유지한다.
인터럽트(Interrupt)
인터럽트는 현재 실행 중인 프로세스와 별개로, 외부에서 발생하는 여러 종료 이벤트(입출력 동작의 종료 등)에 의해 일어난다. 제어가 인터럽트 처리 루틴으로 넘어간 뒤에도 기본적인 시스템 관리 작업은 계속 처리되며, 인터럽트의 형태에 따라 관련된 운영체제 루틴으로 분기한다. 입출력 인터럽트 중 클록 인터럽트는 현재 실행 중인 프로세스의 할당 시간을 검사해, 시간이 다 되면 그 프로세스를 준비 상태로 바꾸고 다른 프로세스를 디스패치해 실행 상태로 전환한다.
트랩(Trap)
트랩은 부적절한 파일 접근처럼, 현재 실행 중인 프로세스 자체에서 발생하는 오류나 예외 상황 때문에 일어난다.


