프로세스(Process)란?
초기에 사용하던 컴퓨터는 프로그램을 한 번에 하나씩 실행했고, 실행 중인 프로그램이 컴퓨터 자원을 독점했다. 반면에 다중 프로그래밍 환경에서는 여러 프로그램을 메모리에 적재하여 병행 실행할 수 있어 컴퓨터의 효율을 높일 수 있다. 병행 실행하는 프로그램들은 컴퓨터 자원을 공유하므로 이를 제어하는 방법이 필요한데, 이 과정에서 해당 포스팅의 주제인 프로세스(Process)가 등장한 것이다.
IBM 운영체제에서는 프로세스를 작업(task)이라고 칭하기도 한다. 프로세스는 다음과 같이 다양하게 정의할 수 있다.
- 실행 중인 프로그램
- 비동기적 행위
- 실행중인 프로시저
- 실행 중인 프로시저의 제어 추적
- 운영체제에 들어 있는 프로세스 제어 블록
- 프로세서에 할당하여 실행할 수 있는 개체 디스패치(dispatch)가 가능한 대상
이중 가장 일반적인 프로세스 정의는 "실행 중인 프로그램"이다. 프로그램이 실행 중이라는 것은 디스크에 있던 프로그램을 메모리에 적재하여 OS의 제어를 받는 상태가 되었다는 것이다. 이는 자신만의 메모리 영역(주소 공간)이 있음을 의미한다. 프로세스는 프로그램 실행의 인스턴스가 되기도 한다.
프로세스가 실행 중인 프로그램이 되려면 프로세서 점유 시간, 메모리, 파일, 입출력 장치 같은 자원이 필요한데, 프로세스를 생성하거나 실행할 때 이 자원을 할당한다. 그리고 프로세스는 현재의 활동 상태를 나타내는 프로그램 카운터, 프로세서의 현재 활동(레지스터 내용)도 포함한다.
프로그램은 컴파일한 코드와 초기화 전역변수, 문자열과 문자열 상수 등 정적 데이터를 포함하는 정적인 개체이다.
반면에 프로세스는 [그림 3-2] 과 같은 메모리 구조를 이루고, 프로그램 카운터나 레지스터처럼 현재 어떤 자원을 사용하는지 관련 정보가 들어 있는 동적인 개체이다.
프로세스의 메모리 구조
스택(stack)
- 스택은 데이터를 일시적으로 저장하는 영역
- 지역변수에 사용하고, 변수가 범위 밖으로 이동하면 공간을 해제
- 호출한 함수의 반환 주소, 반환 값, 매개변수 등에 사용
- 함수를 호출할수록 커지고 반환하면 줄어드는 동적인 영역
- 보통 힙과 인접한 방향으로 점점 커져 스택 포인터와 힙 포인터가 만나면 메모리가 소진되었다는 의미를 갖는다.
힙(heap)
- 힙은 코드 영역과는 별도로 유지되는 자유 영역
- 동적으로 메모리를 할당하려고 프로그램 실행 중 시스템 콜을 사용했다가 해제하는 방법으로 활용
- 프로세스의 공유 라이브러리와 동적으로 적재된 모듈이 서로 공유하는데, 동적 메모리 할당이 발생하면 보통 위쪽으로 커짐.
데이터(data)
- 데이터는 프로그램의 가상 주소 공간
- 전역변수나 정적변수를 저장하거나 할당하고 실행하기 전에 초기화
- 하지만 변수 값은 실행 도중 변경할 수 있어 읽기 전용 영역은 아니지만 읽기 전용 영역이나 쓰기 영역으로 초기화 가능
- 정적변수는 0으로 초기화하거나 초기화 하지 않아도 됨. 초기화하지 않은 데이터는 데이터 영역의 끝에서 시작
코드(code)
- 코드는 실행 명령을 포함하는 메모리이거나 목적 파일 있는 프로그램 영역
- 프로그램을 시작할 때 프로세서가 디스크에서 읽어 실행하는 컴파일한 프로그램을 저장
- 프로세스로 변경할 수 없고, 읽기 전용이므로 프로그램이 코드 영역을 침범하여 쓰기를 시도하면 오류가 발생해서 프로그램을 종료
- 코드 영역은 공유할 수 있으므로 자주 실행하는 워드 프로세서, C 컴파일러, 셸 같은 프로그램의 사본 하나는 메모리에 존재
스택과 힙 사이는 서브루틴을 실행하는 영역으로, OS로 매핑되지 않는다. DB, 문서 편집기처럼 여러 사용자가 메모리에 있는 동일한 사본을 공유할 수 있는 프로그램을 재진입 프로그램이라고 한다. 이들은 프로세스 실행 중에 사용할 데이터를 보관하는 스택 영역과 공통 데이터를 보관하는 데이터 영역으로 나뉘어져 있다. 그래서 프로세스 2개가 동일한 문서 편집기를 사용하고 데이터 영역이 같더라도 스택 영역은 서로 달라 별개의 프로세스로 인식한다.
※ 참고
재진입 프로그램은 메모리의 동일한 사본을 여러 사용자가 공유할 수 있도록 작성한 프로그램이나 루틴이다.
재진입 코드라고도 한다.
프로세스의 종류
사용자 관점에서 프로세스는 주소 공간을 가지고 실행하는 프로그램이고, 시스템 관점에서 프로세스는 위 그림 처럼 실행중인 프로그램이다. 실행 순서를 결정하는 스케줄러는 디스크에 저장된 프로그램에 프로세서를 할당해서 장치나 메모리 같은 파일 자원을 참조한다. 그리고 프로세스를 지원하고 협력하여 교착상태, 보호, 동기화 같은 정보를 교환한다.
프로세스는 수행하는 역할에 따라 시스템(커널) 프로세스와 사용자 프로세스로 구분하고, 병행 수행 방법에 따라 독립 프로세스와 협력 프로세스로 구분한다.
구분 | 종류 | 설명 |
수행 역할 | 시스템(커널) 프로세스 | 모든 시스템 메모리와 프로세서의 명령에 엑세스할 수 있는 프로세스이다. 프로세스 실행 순서를 제어하거나 다른 사용자 및 커널 영역을 침범하지 못하게 감시하고, 사용자 프로세스를 생성하는 기능을 한다. |
사용자 프로세스 | 사용자 코드를 수행하는 프로세스 | |
병행 수행 방법 | 독립 프로세스 | 다른 프로세스에 영향을 주지 않거나 다른 프로세스의 영향을 받지 않으면서 수행하는 병행 프로세스 |
협력 프로세스 | 다른 프로세스에 영향을 주거나 다른 프로세스에서 영향을 받는 병행 프로세스 |
프로세스의 상태 변화와 상태 정보
프로세스의 문맥(Context)
특정 시점을 놓고 봤을 때 이 프로세스가 어디까지 실행을 했는지 가리키는 것이다.
프로세스는 실행이 시작되면 독자적인 주소공간(Address Space)를 형성한다. 이 프로세스가 CPU를 잡게 되면 PC 레지스터(Program Counter Register)가 이 프로세스 코드(Code)의 어느 부분을 가르키게 되고 매순간 인스트럭션(Instruction)을 CPU로 불러와 레지스터에 값을 넣고 ALU(산순논리연산장치)에서 무언가 연산 결과를 레지스터나 메모리에 저장하게 된다.
이 과정을 계속하다가 해당 프로세스가 어디까지 진행해서 와있는가를 규명하는데 필요한 요소들을 프로세스의 문맥(Context, 이하 컨텍스트)라고 한다.
현대의 컴퓨터 시스템(싱글 코어(Single core) 프로세스의 경우)은 프로세스를 번갈아가며 실행하는 시분할 멀티태스킹(Time sharing multitasking)으로 구현된다.
만약 컨텍스트가 없다면 프로그램이 CPU를 놓고 다시 CPU를 잡을 때 앞부분부터 다시 시작하여야 한다. 따라서 프로그램 컨텍스트를 정확하게 파악하고 있어야 어디까지 진행했는지 알 수 있고 다음 인스트럭션을 실행할 수 있다.
프로세스의 문맥은 3가지로 나눌 수 있다.
CPU와 관련된 하드웨어 컨텍스트
CPU의 수행 상태를 나타내는 것으로 프로그램 카운터 값과 각종 레지스터에 저장하고 있는 값들을 의미한다. 현재 시점에 프로세스가 명령을 어디까지 수행했는지 판단하는 지표가 된다.
메모리(Memory)와 관련된 프로그램 주소공간
code, data, stack에 들어 있는 내용이다.
프로세스 관련 커널 자료 구조 (커널 상의 문맥)
PCB (Process Control Block)
운영 체제가 프로세스에게 CPU나 메모리를 얼마나 줘야 하는지, 보안 상으로 취약한 행위를 하고 있지 않은지 관리하는 역할을 하는데, 이때 프로세스마다 상태를 관리하기 위해 PCB를 둔다.
Kernel Stack
시스템 콜을 하면 PC가 커널의 code를 가리키며 수행되는데, 호출 정보를 커널 스택에 프로세스 별로 스택을 두어서 저장한다.
프로세스의 상태
프로세스의 상태는 [그림 3-4]와 같이 크게 실행 상태와 비실행 상태로 구분할 수 있다. OS가 프로세스를 생성하면 비실행 상태로 초기화해서 실행을 기다린다. 실행 중인 프로세스를 종료하거나 인터럽트가 발생하면 비실행 프로세스 중에서 선택한 프로세스를 실행 상태로 바꾼다(디스패치). 이때 인터럽트된 프로세스는 비실행 상태가 된다. 실행 중인 프로세스는 새로운 자원을 할당받으려고 프로세서를 기다리는 비실행 상태로 바뀌기도 한다.
프로세스 상태는 아래 그림처럼 더 세분화할 수 있다. 하지만 중요한 점은 프로세스의 상태를 다르게 표현하지만 어느 한순간 대부분의 프로세스가 준비나 대기 상태로 바뀌면서 단 하나만 실행 상태가 된다는 점이다.
프로세스의 상태 변화는 OS가 작업 스케줄러와 프로세스 스케줄러 같은 프로세서 스케줄러를 이용하여 관리한다.
- 작업 스케줄러
- 스풀러가 디스크에 저장한 작업 중 실행할 작업을 선정하고 준비 리스트에 삽입하여 다중 프로그래밍의 정도를 결정
- 프로세스 스케줄러
- 위 그림과 같이 선정한 작업의 상태를 변화시키며 프로세스의 생성에서 종료까지 과정을 수행
실행 상태의 프로세스가 프로세서를 자발적으로 반환하기 전에 할당된 시간이 지나면 이 프로세스는 준비 상태가 된다. 그리고 프로세스를 실행하다 입출력 명령(또는 시간이 많이 필요한 비슷한 요청)이 발생하면 대기 상태가 된다. 대기 상태인 프로세스는 대기 원인을 제거하면 준비상태로 바뀌고, 디스패처(dispatcher)가 준비 상태인 프로세스에 프로세서를 할당하면 다시 실행 상태로 바뀐다.
여기서 디스패처는 위 그림처럼 스케줄러가 선택한 프로세스에 프로세서를 할당하는 모듈이다.
아래는 각 상태에 대한 정리이다. 그림은 단지 한글과 영어 차이일 뿐 동일한 그림이다.
시작(New)
- 프로세스가 막 생성 중인 상태를 말한다.
준비(Ready)
- CPU를 기다리는 상태이다. 마냥 기다리는 것이 아니라 메모리 등 다른 조건이 모두 만족하고 대기하는 상태가 Ready이다. 보통 Running과 Ready를 번갈아가면서 CPU를 잡았다가 놓았다가 왔다갔다하며 Time sharing system을 구현한다.
- 커널 주소공간의 데이터 영역에 큐(Queue)로 구현되며 프로세스가 Ready 상태로 변경되면 큐에 들어간다.
실행 (Running)
- CPU를 잡고 인스트럭션을 실행 중인 상태를 말한다.
Blocked(Wait, Sleep)
- CPU를 넘겨 줘봤자 당장 인스트럭션을 실행하지 못하는 상태이다. 프로세스 자신이 요청한 이벤트(예: 입・출력)가 즉시 만족되지 않아 이를 기다리는 상태이다. 아직 CPU 할당시간이 남아있지만 자발적으로 프로세스를 중지시킨 상태이다.
- 요청한 이벤트가 종료되면 Ready 상태로 변경(Ready Queue에 들어가게)된다.
종료(Terminated)
- 프로세스가 종료된 상태를 말한다. 주어진 인스트럭션을 모두 수행하고 마지막 정리 중인 상태이다.
Suspended(Stopped)
- 외부적인 이유로 프로세스 수행이 중지된 상태이다. 프로세스는 통째로 디스크에 Swap out된다.
- Blocked와 비슷하여 햇갈릴 수 있는데 Blocked는 자신이 요청한 이벤트를 위하여 자발적으로 프로세스를 정지시킨 것이고 Suspended는 자의적이지 않은 외부적인 요인(예: 사용자가 중단시킨 경우)에 의하여 중지된 상태이다.
- Suspended는 요청한 입・출력이 끝나면 Ready 상태로 돌아가는 Blocked와 달리 외부에서 Resume해야 Active 상태로 돌아간다.
프로세스 상태 변화
프로세스 상태 전이란 프로세스가 실행되는 동안 상태가 OS에 의해 변경되는 것을 말한다. 운영체제는 프로세스의 상태를 감시하고, 프로세스 상태를 기반으로 프로세스 스케쥴링을 통해 프로세스를 관리하고 제어한다.
예를 들어, READY 상태에 있는 여러 프로세스 중에서 어떤 프로세스를 RUNNING 상태로 바꿀지, TERMINATED 상태에 있는 프로세스를 제거하고 READY 상태에 있는 다른 프로세스를 선택할지 스케쥴링 알고리즘에 의해 동작된다.
프로세스의 상태별 변화는 아래 표와 같다. 이 중 프로세스 스스로 하는 것은 대기뿐이고, 나머지는 외부 조건으로 발생한다.
- 디스패처에 의한 준비 (Ready) -> 실행 (Running)
- Ready Queue 맨 앞에 있던 프로세스가 프로세서를 점유하는 것을 디스패치라고 함.
- 다중 프로그래밍 OS에서는 실행 상태인 프로세스가 할당된 시간만큼만 프로세서를 사용하도록 하여 특정 프로세스가 프로세서를 계속 독점하는 것을 방지
- 타임아웃에 의한 실행 (Running) -> 준비 (Ready)
- OS는 실행 상태의 프로세스가 프로세서를 독점하지 않도록 인터럽트 클록(interrupt clock)을 두어 특정 프로세스가 할당된 시간 동안만 프로세서를 점유하게 한다.
- 타임아웃되어도 프로세서를 반환하지 않으면 클록이 인터럽트를 발생시켜 OS에 프로세서 제어권을 부여한다.
- 타임아웃에 의한 인터럽트가 발생하면 실행 상태의 프로세스는 Ready Queue의 가장 뒷단으로 삽입되고 Ready Queue의 첫 번째 프로세스는 Running 상태가 된다.
- Block에 의한 실행 (Running) -> 대기 (Wait)
- 할당된 시간 이전에 실행 상태의 프로세스에 IO 연산이 필요하거나 새로운 자원 요청 등이 발생하면 프로세스는 스스로 프로세서를 양도하고 대기 상태가 된다.
- Wakeup에 의한 대기 (Wait) -> 준비 (Ready)
- 프로세스는 입출력 작업이 끝나면 wake up으로 대기에서 준비 상태가 된다.
프로세스의 상태 변환 예시
running 상태의 프로세스가 사용자로부터 키보드 입력을 받아서 결과를 확인하고 실행한다고 가정해 보자. 그러면 키보드 I/O 큐에 줄을 서게 된다. 즉, running 상태에서 blocked 상태로 변경된다. 그동안 CPU는 ready 상태에 있는 다른 프로세스에게 CPU를 전달한다.
키보드 입력을 받으면 키보드 컨트롤러가 CPU에게 인터럽트를 걸어서 알려주고, CPU는 하던 일을 멈추고 운영체제에게 넘어가서 프로세스 상태를 ready로 바꾼다. 즉, 키보드 입력이 들어왔기 때문에 CPU를 얻는 권한이 생기며 Ready 큐에 줄을 서게 된다.
이외에 프로세스는 공유 데이터에 접근하거나, 디스크, 키보드, 디스크 등 종류에 따라 그에 맞는 큐에 줄을 서게 된다.
PCB(Program Control Block)
운영체제가 각 프로세스를 관리하기 위해 프로세스당 유지하는 정보를 말한다. 커널 주소공간의 데이터 영역에 포함되며 다음의 구성요소를 가지며 구조체로 유지된다. 또한 프로세스가 생성되면 메모리에 PCB를 생성하고, 프로세스가 실행을 종료하면 해당 PCB를 삭제한다.
위의 컨텍스트에 대한 내용을 보면 알 수 있듯이 아래의 2~4번은 컨텍스트와 관련된 정보이다.
- OS가 관리 상 사용하는 정보
- 프로세스 상태, 프로세스 ID, CPU 스케줄링 정보, 프로세스 우선 순위 등
- CPU 수행 관련 하드웨어 값
- Program counter 값, registers 값
- 메모리 관련
- Code, Data, Stack의 위치 정보
- 파일 관련
- 열려 있었던 파일의 설명 등
컨텍스트 전환(Context Switch)
컨텍스트 스위치는 CPU 제어권들 한 프로세스에서 다른 프로세스로 넘겨주는 과정을 말한다.
예를 들어 사용자 프로세스가 CPU를 할당 받고 실행되던 중에 타이머 인터럽트가 발생하면 CPU의 제어권은 운영체제에게 넘어가게 된다. 그러면 운영 체제는 타이머 인터럽트 처리 루틴으로 가서 직전까지 수행 중이던 프로세스의 문맥을 저장하고 새롭게 실행 시킬 프로세스에게 CPU의 제어권을 넘긴다. 이 과정에서 원래 수행 중이던 프로세스는 준비 상태로 바뀌고 새롭게 CPU를 할당 받은 프로세스는 실행 상태가 된다.
문맥 교환 중에 원래 CPU를 보유하고 있던 프로세스는 프로그램 카운터 값과 레지스터 값, 메모리 맵 등 프로세스의 문맥을 자신의 PCB에 저장하고, 새롭게 CPU를 할당 받을 프로세스는 예전에 저장했던 자신의 문맥을 PCB로부터 실제 하드웨어로 복원하는 과정을 거친다.
시스템 콜과 인터럽트는 모두 컨텍스트 전환을 발생시키는가
그럴 수도 있고 아닐 수도 있다.
1번 경우는 프로세스가 실행 상태일 때 시스템 콜이나 인터럽트가 발생하여 CPU의 제어권이 운영 체제로 넘어와 원래 실행 중이던 프로세스의 업무가 잠시 멈추고 운영체제 커널의 코드가 실행된 경우다.
이 경우에도 CPU의 실행 위치 등 프로세스의 문맥 중 일부를 PCB에 저장한다. 하지만, 하나의 프로세스의 실행 모드가 사용자 모드에서 커널 모드로 바뀌는 것이고 CPU를 점유하는 프로세스가 다른 사용자 프로세스로 변경되는 것이 아니므로 문맥 교환이 아니다.
2번 경우는 타이머 인터럽트가 발생하면 CPU를 다른 프로세스에 할당하며 CPU에 비해 처리속도가 매우 느린 I/O 요청 시스템 콜의 경우에도 해당 프로세스를 Blocked로 변경한 뒤 운영체제가 CPU를 다른 프로세스에 할당한다. 이 경우에는 컨텍스트 전환이 발생한다.
전자의 경우에도 CPU 수행정보 등 컨텍스트의 일부는 PCB에 저장하는 과정이 있긴하지만 후자의 경우 모든 컨텍스트를 저장하고 캐쉬 메모리(Cache Memory)를 Flush 하는 등 오버헤드(Over Head)가 훨씬 크다.
프로세스의 관리
프로세스 구조
OS는 프로세스에서 생성, 종료, 제거, 중단(서스펜드, suspend), 재시작, 우선순위 변경, 대기, 문맥교환 등 다양한 작업을 수행할 수 있다.
프로세스는 실행 중에 프로세스 생성 시스템 콜을 이용하여 새로운 프로세스를 생성할 수 있다. 이때 프로세스 생성 순서를 저장하고 부모-자식 관계를 유지하여 계층적으로 생성한다.
프로세스를 새로 생성하는 프로세스는 부모 프로세스(parent process)이고, 생성되는 프로세스는 자식 프로세스(child process) 또는 서브 프로세스(subprocess)이다. 부모 프로세스는 자식 프로세스를 생성하는 과정을 반복하면서 계층 구조를 형성한다.
프로세스 생성
OS나 응용 프로그램에서 요청을 받아 프로세스를 생성하면, OS는 해당 프로세스에 대한 PCB를 만들고 주소 공간을 할당한다.
- 일관 처리 환경
- 준비 큐에 작업이 도착할 때 프로세스를 생성
- 대화형 환경
- 새로운 사용자가 로그온(log-on)할 때 프로세스를 생성
예를 들어, 사용자 요청에 따라 프린터를 구동하는 프로세스를 생성하면 해당 프로세스의 식별자를 결정하고, 우선 순위를 부여하고, PCB를 생성(할당된 자원, 자원 리스트 등 정보 기록)하고, 초기 자원을 할당한 후 준비 상태가 되어 준비 리스트의 마지막에 위치한다.
- 새로운 프로세스에 프로세스 식별자를 할당
- 프로세스의 모든 구성 요소를 포함할 수 있는 주소 공간과 PCB 공간을 할당
- PCB를 초기화한다. 프로세스 상태, 프로그램 카운터 등 초기화, 자원 요청, 프로세스 제어 정보(우선순위) 등을 포함.
- 링크를 건다(해당 큐에 삽입한다.)
프로세스가 작업을 수행하려면 프로세서 점유 시간, 메모리, 파일, IO 장치 등 자원이 필요하다. 자식 프로세스는 OS에서 직접 자원을 할당 받거나 부모 프로세스의 자원을 일부 사용할 수 있다. 이때 부모 프로세스는 자식 프로세스가 사용하는 자원을 제한해서 특정 프로세스가 또 다른 자식 프로세스를 너무 많이 생성하여 시스템에 부담을 주는 것을 막을 수 있다.
프로세스를 생성할 때는 이런 자원 외에도 일부 초기화 데이터를 부모 프로세스에서 자식 프로세스로 전달하기도 한다.
예를 들어, P 파일의 상태를 단말기에 표기하는 P1 프로세스가 있다고 하자. P1 프로세스를 생성하면 부모 프로세스에서 이름이 P 인 파일을 입력받아 필요한 정보를 얻고 출력장치의 기능도 전달받을 수 있다.
또한 프로세스가 새로운 프로세스를 생성할 때 다음 두 가지 실행이 발생할 수 있다.
- 부모 프로세스와 자식 프로세스를 동시에 실행
- 부모 프로세스는 자식 프로세스를 모두 종료할 때까지 기다림
fork 명령어를 사용하는 경우는 크게 두 가지이다.
- 부모와 동일한 자식을 복제
- fork 명령어를 호출한 후 exec 명령어를 연달아 호출하여 자식 프로세스의 주소 공간을 별도 프로그램 주소 공간으로 덮어씌우기
유닉스 예시
유닉스에는 각 프로세스마다 정수로 된 고유한 프로세스 식발자가 있다. fork 명령어로 새로운 프로세스를 생성하면 부모 프로세스의 주소 공간을 복사하여 부모 프로세스와 자식 프로세스의 정보를 쉽게 교환할 수 있다. 부모 프로세스와 자식 프로세스가 fork 명령어를 동시에 수행하여 실행을 계속하면 자식 프로세스는 식별자가 0이 되고, 자식 프로세스의 식별자는 부모 프로세스의 식별자가 된다.즉, fork 명령러를 실행하면 자식 프로세스에는 0을 반환하지만, 부모 프로세스에는 자식 프로세스의 식별자를 반환한다.
프로세스 종료
프로세스가 마지막 명령을 실행하면 종료하여 OS에 프로세스의 삭제를 요청한다.
- 일관 처리 환경
- 작업 종료를 의미하는 신호로 인터럽트를 발생시키거나 시스템 콜로 중단 명령을 전달하여 프로세스 종료
- 대화형 환경
- 사용자가 로그오프(log-off)하거나 터미널을 닫으면 프로세스 종료
다른 예시로 abort 명령어로도 프로세스를 종료할 수 있다. abort 명령어는 종료할 프로세스의 부모 프로세스만 호출하는데, 부모가 아닌 다른 프로세스가 임의로 해당 프로세스를 중단할 수 없기 때문이다. 그리고 자식 프로세스는 종료할 때 자신의 식별자를 부모 프로세스에 전달한다.
부모 프로세스는 다음 상황에서 자식 프로세스를 종효할 수 있다.
- 자식 프로세스가 할당된 자원을 초과하여 자원을 사용할 때
- 자식 프로세스에 할당한 작업이 더는 없을 때
보통 부모 프로세스를 종료하면 자식 프로세스는 필요하지 않기 때문에 OS가 자식 프로세스도 종료하는데 이를 연속 종료라고 한다.
※ 참고
유직스에서는 exit 명령어로 프로세스를 종료하고, 부모 프로세스는 wait 명령어를 사용하여 자식 프로세스의 종료를 기다린다. wait 명령어는 종료한 자식 프로세스의 식별자를 부모 프로세스에 돌려주므로 어떤 자식 프로세스를 종료했는지 알려 준다.
프로세스는 다양한 이유로 종료할 수 있는데, 다음은 몇가지 예이다.
- 정상 종료 : 프로세스가 OS의 서비스를 호출할 때
- 시간 초과 : 프로세스가 명시된 전체 시간을 초과하여 실행하거나 명시된 시간을 초과함녀서 어떤 이벤트 발생을 기다릴 때
- 실패 : 파일 검색 실패, 입출력이 명시된 횟수를 초과하여 실패할 때
- 산술 오류, 보호 오류, 데이터 오류 등
- 메모리 부족, 액세스 위반 등
프로세스 제거
프로세스 제거는 프로세스를 파괴하는 것이다. 프로세스를 제거하면 사용하던 자원을 시스템에 돌려주고, 해당 프로세스는 시스템 리스트나 테이블에서 사라져 PCB을 회수한다. 하지만 프로그램은 여전히 디스크에 저장한다. 그리고 자식 프로세스는 부모 프로세스를 제거하면 자동으로 제거된다.
프로세스 중단과 재시작
앞서 살펴본 프로세스의 준비, 실행, 대기 상태만 이용하면 IO 동작이 일반 연산보다 느리므로 시스템이 대부분 유휴(idle) 상태이다.
다중 프로그래밍 환경에서도 프로세서의 동작시간이 IO보다 짧아 프로세스 Context Switching이 일어난 후에도 기다리게 되므로 대부분 유휴(idle) 시간이 된다. 이는 윈도우에서 다수의 응용 프로그램을 실행할 때 현재 사용 중인 윈도우 외에는 대기 상태이어서 비활성화 상태가 되는 것과 같다.
이러한 시스템의 유휴(idle) 시간 문제를 해결하기 위한 것이 프로세스 중단(일시정지) 상태를 이용하는 것이다.
OS는 새로운 프로세스를 생성하여 실행하거나 실행 중인 프로세스를 중단했다가 다시 실행하여 사용할 수 있다. 그리고 중단 상태로 인해 시스템 전체의 부하를 증가시키지 않으면서 프로세스에 서비스를 제공할 수 있다. 실행에서 대기가 아닌 중단 상태를 추가하면 특정 이벤트의 발생을 기다리면서 대기 상태가 된다. 그러면 해당 이벤트가 발생할 때 즉시 실행 상태로 바꿀 수 있는 이점이 있다.
다중 프로그래밍에서 중단은 프로세스 입출력 요구 외에 다른 원인으로 프로세스가 실행을 중단한 상태, 즉 자원 부족(대기) 상태를 의미하기도 한다. 물론 중단된 프로세스는 기다리던 이벤트(ex. IO 완료 인터럽트가 발생)가 발생하면 중단 이전의 상태로 되돌아갈 수 있다.
프로세스 중단과 재시작은 시스템 부하를 조절하는데 상당히 중요하고, 다음 상황에서 주로 발생한다.
- 시스템에 장애가 발생하면 실행 중인 프로세스는 잠시 중단했다가 시스템이 기능을 회복할 때 다시 시작할 수 있다.
- 프로세스에 의심스러운 부분이 있으면 실행 중인 프로세스를 중단하여 확인한 수 재시간하거나 종료할 수 있다.
- 처리할 작업이 너무 많아 시스템에 부담이 되면(너무 많은 적재) 프로세스 몇개를 중단했다가 시스템이 정상 상태로 다시 돌아왔을 때 재시작할 수 있다.
프로세스 대기 vs 중단 상태
대대수 시스템은 프로세스를 실행하기 전에 자원을 할당받고 실행을 시작하지만, 다중 프로그래밍 환경에서는 자원의 이용률과 시스템 효율을 높이려고 자원을 동적으로 할당한다. 이때 자원을 할당받으려고 기다리는 상태가 "대기" 이고 할당받은 자원을 기다리는 상태가 "중단"이다.
예를 들어 프로세스는 더 많은 저장장치를 요구하는데, 사용 가능한 저장장치가 부족하면 대기 상태가 된다. 대기 상태는 대부분 시간이 지나면 요청한 자원을 사용할 수 있어 "준비" 상태가 된다. 그러나 하드웨어가 고장 나거나 다른 프로세스가 해당 자원을 반환할 수 없는 상태, 즉 교착 상태가 되면 문제가 발생한다.
중단된 준비 vs 대기 상태
중단은 프로세스 자신이나 다른 프로세스로 가능하다.
- 단일 처리 시스템
- 해당 프로세스 스스로 중단해야 한다.
- 다중 처리 시스템
- 다른 프로세서가 실행 중인 프로세스를 중단할 수 있다. 중단된 프로세스는 다른 프로세서가 재시작하기 전에는 실행할 수 없다.
중단은 아주 중요한 작업이다. 보통 짧은 시간에 너무 많은 프로세스를 요청하면 프로세스 몇개가 중단된다. 장시간 중단할 때는 해당 프로세스에 할당된 자원을 다시 돌려주어야 하는데, 자원의 성질에 따라서 돌려줄 자원을 결정한다.
- 메인 메모리
- 프로세서를 중단하자마자 돌려주어야 한다.
- 보조 메모리
- 중단 시간을 예측할 수 없거나 너무 길 때 돌려주어야 한다.
- 중단된 대기 상태
- 프로세스가 보조 메모리에 있고 이벤트를 대기 중인 상태이다.
- 중단된 준비 상태
- 프로세스가 보조 메모리에 있지만 즉시 메인 메모리로 적재하여 실행할 수 있는 상태이다.
물론 중단한 프로세스는 중단한 지점부터 다시 시작한다.
프로세스 우선순위 변경
프로세스 스케줄러는 PCB에 있는 우선 순위를 이용하여 Ready Queue의 프로세스를 처리한다. Ready Queue의 프로세스는 프로세서 중심 프로세스와 IO 중심 프로세서로 구분할 수 있다.
IO 중심 프로세서
- 속도가 느리면서 빠른 응답을 요구하는 단말기 IO 프로세스에 높은 우선순위를 부여
- 속도가 빠른 디스크 IO 프로세스에는 낮은 우선순위를 부여
이때 우선순위가 낮은 프로세스에는 시간을 많이 할당하고, 우선순위가 높은 프로세스에는 시간을 적게 할당한다.
따라서 IO 중심 프로세스는 프로세서를 짧게 자주 사용하도록 하고, 프로세서 중심 프로세스는 프로세서를 길게 사용하되 사용 횟수를 줄여서 균형을 유지할 수 있다.
예시
사람이 타이핑 하면 화면에 빨리 나와야 한다. 하지만 디스크에서 읽어들이고 출력하는 것은 느려도 된다.
즉, 응답에 관해서는 좀 느려도 된다.
입출력 중심 프로세서는 프로세스 자체가 빨리 끝나는 건가?
프로세스 사용했다가 - 입출력 사용했다가 -프로세스 사용했다가 - 입출력 사용했다가 하는 것이다.
그런데 프로세스를 사용하는 시간이 짧은 것이다.
한번 cpu를 사용하는 시간이 짧다는 것이다.
프로세스 Context Switching
현재 실행하는 프로세스와 별도로 외부에서 이벤트(ex. IO 동작의 종료) 가 일어나면 인터럽트가 발생한다. 보통 인터럽트가 발생하면 인터럽트 처리 루틴으로 제어가 넘어간 후에도 시스템 관리와 관련된 기본 작업을 하고는 인터럽트 유형에 따라 관련 루틴으로 분기된다.
- IO 인터럽트
- IO 동작이 발생했음을 확인하고 이벤트를 기다리는 프로세스를 준비 상태로 바꾼 후 실행할 프로세스를 결정
- 클록 인터럽트
- 현재 실행 중인 프로세스의 할당 시간을 조사하여 실행 중인 프로세스를 준비 상태로 바꾸고, 다른 프로세스를 실행 상태로 바꿈
실행 중인 프로세스에 인터럽트가 발생하면 OS가 다른 프로세스를 실행 상태로 바꾸고 제어를 넘겨주어 [그림3-11]처럼 문맥 교환이 일어난다.
- 인터럽트
- 인터럽트 처리 루틴을 실행한 후 현재 실행 중인 프로세스를 재실행할 수 있으므로 인터럽트가 곧 프로세스 문맥 교환으로 발전하지는 않는다.
- 트랩(trap)
- 부적절한 파일 접근이나 현재 실행 중인 프로세스 오류나 예외 상행으로 발생할 수 있다. 시스템에 치명적인 오류인지 판단하여 치명적이면 프로세스를 종료하고 교환한다.
정의
대개 이전 프로세스의 상태 레지스터 내용을 보관하고 다른 프로세스의 레지스터를 적재하여 프로세스를 교환하는데, 이런 일련의 과정을 문맥교환(context switching)이라고 한다.
문맥 교환은 프로세스 아래의 상태들로 바뀔 때 발생한다.
- "준비 → 실행"
- "실행 → 준비"
- "실행 → 대기"
또한 문맥 교환에서는 오버헤드가 발생하는데, 이는 메모리 속도, 레지스터 수, 특수 명령어의 유뮤에 따라 다르다.
현재 실행 중인 프로세스 Pj 에서 문맥 교환을 요청하면, 우선 사용자 모드에서 커널 모드로 Pj의 제어가 넘어가면서 프로세스 Pj를 종료한다. 그리고 다시 시작할 수 있도록 Pj의 현재 하드웨어 상태를 PCBj에 저장하고 다음에 실행할 프로세스로 Pk를 선택한다. Pk 정보를 PCBk에서 얻어 와 프로세서에 재저장하고는 사용자 모드로 돌아와서 프로세스 Pk를 실행한다.
※ 참고
PCB(프로세스 제어 블록)는 프로세스를 관리하려고 유지하는 데이터 블록 또는 레코드의 데이터 구조이다.
문맥 교환은 시간 비용이 들어가는 오버헤드이고, 이 오버헤드는 메모리 속도, 레이스터 수, 특수 명령어의 유무에 따라 시스템마다 다르다.
또한 문맥 교환은 레지스터, 작업, 스레드, 프로세스 문맥 교환이 가능하다. 프로세스와 OS가 문맥의 구성 여부를 결정하므로 OS를 설계할 때 주요 관심사는 가능한 한 불필요한 문맥 교환을 줄이는 것이다. 하지만 이는 실제로 구현하기가 쉽지 않으며 그래서 스레드를 이용하여 문맥 교환을 효율적으로 처리할 수 있다.
참고