[OS] 프로세스 관리
@notion
<운영체제와 정보기술의 원리> 반효경 저
1. 프로세스의 개념
디스크에 실행파일 형태로 존재하던 프로그램이 메모리에 올라가서 실행되기 시작하면 비로소 생명력을 갖는 프로세스가 된다.
- 프로세스(process): 실행 중인 프로그램(program in execution)
CPU를 다시 획득하여 명령의 수행을 재개할 때 이전의 CPU를 보유했던 시점의 정확한 상태를 재현해야 한다.
- 프로세스 문맥(context): 프로세스가 현재 어떤 상태에서 수행되고 있는지에 대한 정보
프로세스의 문맥을 크게 하드웨어 문맥, 프로세스의 주소 공간, 커널상의 문맥으로 나눌 수 있다.
- 하드웨어 문맥: CPU의 수행 상태를 나타내는 것으로 프로그램 카운터값과 각종 레지스터에 저장하고 있는 값들
- 프로세스의 주소 공간: 코드, 데이터, 스택으로 구성되는 프로세스 자신만의 독자적인 주소 공간
- 커널스택: 프로그램이 수행되어 프로세스가 됐을 때 프로세스를 관리하기 위해 운영체제가 유지하는 자료구조
2. 프로세스의 상태
컴퓨터의 자원을 효율적으로 관리하기 위해 프로세스의 상태를 실행(running), 준비(ready), 봉쇄(blocked, wait, sleep) 세 가지로 구분한다.
- 실행: 프로세스가 CPU를 보유하고 기계어 명령을 실행하고 있는 상태
- 준비: 프로세스가 CPU만 보유하면 당장 명령을 실행할 수 있지만 CPU를 할당받지 못한 상태
- 봉쇄: CPU를 할당받더라도 당장 명령을 실행할 수 없는 상태
- e.g. 프로세스가 요청한 입출력 작업이 진행 중인 경우
프로세스가 생성 중이거나 종료 중인 일시적 상태를 각각 시작(new) 상태, 완료(terminated) 상태라고 부르기도 한다.
- 시작 상태: 프로세스가 시작되어 그 프로세스를 위한 각종 자료구조는 생성되었지만 아직 메모리 획득을 승인받지 못한 상태
- 완료 상태: 프로세스가 종료되었으나 운영체제가 그 프로세스와 관련된 자료구조를 완전히 정리하지 못한 상태
3. 프로세스 제어블록
- 프로세스 제어블록(Process Control Block, PCB): 운영체제가 시스템 내의 프로세스들을 관리하기 위해 프로세스마다 유지하는 정보들을 담는 커널 내의 자료구조
PCB는 다음과 같은 요소들로 구성되어 있다.
- 프로세스의 상태(process state): CPU를 할당해도 되는지 여부를 결정
- 프로그램 카운터값: 다음에 수행할 명령의 위치를 가리킴
- CPU 레지스터값: CPU 연산을 위해 현 시점에 레지스터에 저장하고 있는 값
- CPU 스케줄링 정보: 프로세스의 CPU 스케줄링에 필요한 정보
- 메모리 관련 정보: 메모리 할당을 위해 필요한 정보
- 자원 사용 정보: 사용자에게 자원 사용 요금을 계산해 청구하는 등의 용도
- 입출력 상태 정보: 프로세스가 오픈한 파일 정보 등 프로세스의 입출력 관련 상태 정보
4. 문맥교환
- 문맥교환(context switch): 하나의 사용자 프로세스로부터 다른 사용자 프로세스로 CPU 제어권이 이양되는 과정
사용자 프로세스가 CPU를 할당받고 실행되던 중에 타이머 인터럽트가 발생하면 CPU의 제어권은 운영체제로 넘어가게 된다. 운영체제는 타이머 인터럽트 처리루틴으로 가서 직전까지 수행 중이던 프로세스의 문맥을 저장하고 새롭게 실행시킬 프로세스에게 CPU를 이양한다. 이 과정에서 원래 수행 중이던 프로세스는 준비 상태로 바뀌고 새롭게 CPU를 할당받은 프로세스는 실행 상태가 된다. 문맥교환 중에 원래 CPU를 보유하고 있던 프로세스는 프로세스의 문맥을 자신의 PCB에 저장하고, 새롭게 CPU를 할당받을 프로세스는 예전에 저장했던 자신의 문맥을 PCB로부터 실제 하드웨어로 복원시킨다.
프로세스가 실행 상태일 때 시스템 콜이나 인터럽트가 발생하면 CPU의 제어권이 운영체제로 넘어와 원래 실행 중이던 프로세스의 업무를 잠시 멈추고 운영체제 커널의 코드가 실행된다. 이 경우에도 CPU의 실행 위치 등 프로세스의 문맥 일부를 PCB에 저장하지만 이를 문맥교환이라 하지는 않는다. 하나의 프로세스의 실행모드만이 사용자모드에서 커널모드로 바뀌는 것일 뿐, CPU를 점유하는 프로세스가 다른 사용자 프로세스로 변경되는 과정이 아니기 때문이다. 이와 같은 모드 변경에 비해 문맥교환에는 훨씬 많은 오버헤드가 뒤따르므로 적절한 CPU 할당시간을 정하는 것이 중요하다.
5. 프로세스를 스케줄링하기 위한 큐
프로세스의 상태 관리는 커널의 주소 영역 중 데이터 영역에 다양한 큐(queue)를 두어 수행하게 된다. 커널은 준비 큐의 정보를 참조해 다음에 어느 프로세스에게 CPU를 할당할지 결정한다.
운영체제는 준비 상태에 있는 프로세스들을 줄 세우기 위해 준비 큐를 두고 준비 큐의 제일 앞에 있는 프로세스에게 제일 먼저 CPU를 할당한다. 준비 큐 외에도 운영체제는 특정 자원을 기다리는 프로세스들을 줄 세우기 위해 자원별로 장치 큐를 둔다. 예를 들어 디스크에 입출력 서비스를 요청한 프로세스들은 디스큐 입출력 큐에 줄 서고, 작업 수행 후에는 다시 CPU를 기다리는 준비 큐에 줄 서게 된다.
이렇게 하드웨어 자원을 기다릴 때뿐만 아니라 큐는 소프트웨어 자원을 기다리는 경우에도 필요하다. 예를 들어 공유 데이터는 매 시점 하나의 프로세스만이 접근할 수 있도록 해야하므로, 여러 프로세스가 공유 데이터에 동시에 접근하려고 할 경우 공유 데이터를 기다리는 큐에 줄 서게 한다.
운영체제는 준비 큐와 장치 큐 외에 작업 큐를 추가로 유지한다.
- 작업 큐: 시스템 내의 모든 프로세스를 관리하기 위한 큐로, 프로세스의 상태와 무관하게 현재 시스템 내에 있는 모든 프로세스가 작업 큐에 속하며 이들은 봉쇄상태에 있음
6. 스케줄러
- 스케줄러: 어떤 프로세스에게 자원을 할당할지 결정하는 운영체제 커널의 코드
- 장기 스케줄러(long term scheduler): 작업 스케줄러라고도 부르고, 어떤 프로세스를 준비 큐에 진입시킬지 결정하며 프로세스에 메모리를 할당하는 문제에 관여
- 가끔 호출되기 때문에 상대적으로 속도가 느린 것이 허용
- 메모리에 동시에 올라가 있는 프로세스의 수 조절
- 단기 스케줄러(short term scheduler): CPU 스케줄러라고도 하며, 준비 상태의 프로세스 중에서 어떤 프로세스를 다음번에 실행 상태로 만들 것인지(CPU를 할당할 것인지) 결정
- 빈번하게 호출되므로 수행 속도가 충분히 빨라야 함
- 시분할 시스템에서 타이머 인터럽트가 발생하면 단기 스케줄러 호출
- 중기 스케줄러(medium term scheduling): 너무 많은 프로세스에게 메모리를 할당해 시스템의 성능이 저하되는 경우 이를 해결하기 위해 메모리에 적재된 프로세스의 수를 동적으로 조절하기 위해 추가된 스케줄러
- 시분할 시스템용 운영체제에서 주로 사용
- 프로세스당 보유 메모리양이 지나치게 적어진 경우 이를 완화시키기 위해 일부 프로세스를 메모리에서 디스크로 스왑 아웃시킴
- 장기 스케줄러(long term scheduler): 작업 스케줄러라고도 부르고, 어떤 프로세스를 준비 큐에 진입시킬지 결정하며 프로세스에 메모리를 할당하는 문제에 관여
중기 스케줄러의 등장으로 프로세스의 상태에는 실행, 준비, 봉쇄 외에 외부적인 이유로 프로세스의 수행이 정지된 상태를 나타내는 중지(suspended, stopped) 상태가 추가됐다. 중지 상태에 있는 프로세스는 외부에서 재개시키지 않는 이상 다시 활성화될 수 없으므로 메모리 자원이 당장 필요하지 않다. 따라서 디스크로 스왑 아웃된다.
중지 상태는 중지준비 상태와 중지봉쇄 상태로 세분화한다.
- 중지준비 상태: 준비 상태에 있던 프로세스가 중기 스케줄러에 의해 스왑 아웃될 때
- 중지봉쇄 상태: 봉쇄 상태에 있던 프로세스가 중기 스케줄러에 의해 스왑 아웃될 때
7. 프로세스의 생성
시스템이 부팅된 후 최초의 프로세스는 운영체제가 직접 생성하고, 그다음부터는 이미 존재하는 프로세스가 다른 프로세스를 복제 생성한다.
- 전자를 부모 프로세스, 후자를 자식 프로세스라고 한다.
프로세스가 수행되는 모델에는 부모와 자식이 공존하며 수행되는 모델과 자식이 종료될 때까지 부모가 기다리는 모델이 있다. 전자에서는 자식과 부모가 같이 CPU를 획득하기 위해 경쟁하는 관계가 되고, 후자에서는 자식 프로세스가 종료될 때까지 부모 프로세스는 아무 일도 하지 않고 봉쇄 상태에 있다가 자식 프로세스가 종료되면 그때 준비 상태가 되어 다시 CPU를 얻을 권한이 생긴다.
원칙적으로 부모 프로세스가 종료되기 전에 그 아래 존재하는 모든 자식 프로세스들이 먼저 종료돼야 한다. 이와 관련해 프로세스 종료는 두 가지로 나뉜다.
- 자발적 종료: 프로세스가 마지막 명령을 수행한 후 운영체제에 exit() 시스템 콜을 통해 이를 알려 이루어짐
- 비자발적 종료: 아래와 같은 상황에서 abort() 함수를 통해 부모 프로세스가 자식 프로세스의 수행을 강제로 종료시키는 것
- 자식 프로세스가 할당 자원의 한계치를 넘어서는 많은 양의 자원을 요구할 때
- 자식 프로세스에게 할당된 작업이 더 이상 필요하지 않을 때
- 부모 프로세스가 종료되는 경우
부모 프로세스가 메인함수의 첫 번째 줄부터 코드를 수행하다가 fork() 시스템 콜에 이르면 CPU의 제어권이 커널로 넘어가고 커널은 fork()를 호출한 프로세스를 복제해 자식 프로세스를 생성한다. 이 프로세스는 부모 프로세스와 모든 문맥을 동일하게 가진다. 이 프로세스가 복제라는 사실을 알 수 있는 단서 하나는 fork() 함수의 결괏값으로 원본에게는 양수를 주고 복제본에게는 0을 준다는 점이다. 이를 이용해 조건문을 사용하여 원본과 복제본 프로세스가 다른 작업을 하도록 프로그램을 작성할 수 있다.
하지만 이는 조건문에 의한 분기일 뿐 사실상 두 프로세스 모두 동일한 코드의 내용을 가질 수밖에 없다. 따라서 자식 프로세스에게 부모와는 다른 독자적인 프로그램을 수행시킬 수 있는 메커니즘이 필요하다. 유닉스에서는 프로세스의 주소 공간에 새로운 프로그램을 덮어씌우는 exec() 시스템 콜을 지원한다.
- exec() 시스템 콜: 프로세스가 지금까지 수행했던 상태를 잊어버리고 그 주소 공간을 완전히 새로운 프로그램으로 덮어씌운 후 새로운 프로그램의 첫 부분부터 다시 실행을 시작하도록 하는 시스템 콜
새로운 프로그램을 수행시키기 위해서는 fork()를 통해 기존 프로세스와 동일한 프로세스를 복제한 후 exec()을 통해 새롭게 수행시키려는 프로세스를 자식 프로세스의 주소 공간에 덮어씌우면 된다.
프로세스의 생성과 관련된 fork(), exec() 등은 특권명령에 해당한다.
8. 프로세스 간의 협력
부모 프로세스가 자식 프로세스를 생성할 때 자식 프로세스가 부모 프로세스의 주소 공간을 복제하긴 하지만, 생성이 완료된 후부터는 각자 자신의 독자적인 주소 공간만을 참조해 코드를 수행하는 독립적인 관계가 된다. 경우에 따라서는 독립적인 프로세스들이 협력할 때 업무의 효율성이 증진될 수 있으므로 운영체제는 프로세스 간의 협력 메커니즘을 제공하는데 그 중 대표적인 메커니즘으로는 IPC가 있다.
- IPC(Inter-Process Communication): 하나의 컴퓨터 안에서 실행 중인 서로 다른 프로세스 간에 발생하는 통신
IPC에는 공유 데이터의 사용 여부에 따라 메시지 전달 방식과 공유메모리 방식이 있다.
- 메시지 전달 방식(message passing): 프로세스 간에 공유 데이터를 일체 사용하지 않고 메시지를 주고받으며 통신하는 방식
- 두 프로세스의 주소 공간이 다르므로 메시지 전달을 직접 할 수는 없고 커널이 그 역할을 함
- 통신하기를 원하는 두 프로세스는 커뮤니케이션 링크를 생성한 후, 커널에 의해 send와 receive라는 두 가지 연산(특권 명령)을 제공 받고, 이 연산을 통해 프로세스는 전달할 메시지를 운영체제에게 시스템 콜 방식으로 요청해 전달함
- 직접통신
- 커뮤니케이션 링크는 자동적으로 생성
- 각 쌍의 프로세스에게는 오직 하나의 링크만이 존재
- 간접통신
- 메시지를 메일박스 또는 포트로부터 전달받고, 메일박스를 공유하는 프로세스들만 서로 통신 가능
- 커뮤니케이션 링크는 프로세스 간에 메일박스를 공유하는 경우에만 생성
- 하나의 링크가 여러 프로세스들에 할당될 수 있으며 각 프로세스의 쌍은 여러 링크를 공유할 수 있음
- 공유메모리(shared memory) 방식: 프로세스들이 주소 공간의 일부를 공유하는 방식
- 원칙적으론 서로 독립적인 주소 공간을 갖지만, 운영체제는 공유메모리를 사용하는 시스템 콜을 지원해 여러 프로세스가 읽고 쓰는 것이 가능함
- 서로의 데이터에 일관성 문제가 생길 수 있으므로 프로세스들끼리 직접 공유메모리 접근에 대한 동기화 문제를 책임져야 함