1. 프로세스의 문맥(context)
프로세스는 한 마디로 실행 중인 프로그램을 말합니다.
그런데 이 프로세스를 이해하기 위해서 중요한 것이 하나가 있는데 바로 프로세스의 문맥(context)입니다.
프로세스 문맥에 대해 간단히 설명드리자면, 프로그램이 실행 중 어떤 시점에서의 정확한 상태를 규명하기 위해 필요한 요소들을 프로세스 문맥이라고 합니다. 프로그램 실행 중에 어떤 한 시점을 놓고 봤을 때, 프로세스가 어디까지 무엇을 실행을 했는지, 현재 어떤 단계까지 실행을 한 상태인지를 정확하게 나타내기 위해 사용되는 개념입니다. (맥락을 파악한다고 생각하면 이해가 쉽습니다.)
프로세스의 문맥은 크게 세 가지로 설명할 수 있습니다.
1. CPU 수행 상태를 나타내는 하드웨어 문맥
- Program Counter
- 각종 Register
프로세스는 CPU를 잡고 매 순간 인스트럭션을 실행합니다. 그래서 현재 시점에 이 프로세스가 인스트럭션을 어디까지 실행했는지를 알기 위해서는 레지스터에 어떤 값을 넣고 있었는지, Program Counter가 어디를 가르키고 있었는지 등의 요소가 필요합니다. 그것이 CPU와 관련된 하드웨어 문맥을 뜻합니다.
2. 프로세스의 주소 공간
- code, data, stack
메모리와 관련된 프로세스의 주소 공간입니다. 문맥을 파악하는 시점에 해당 프로세스의 주소 공간, 즉 code, data, stack에 어떤 내용이 들어가있는지 알아야 프로세스의 현재 상태를 나타낼 수 있습니다.
3. 프로세스 관련 커널 자료 구조
- PCB (Process Control Block)
- Kernel stack
운영체제의 역할 중 하나가 프로세스들을 관리하는 것입니다. 프로세스가 하나 생길 때마다 운영체제는 프로세스를 관리하기 위해 자신의 데이터 영역에 PCB(Process Controll Block)라는 자료구조를 두고 있습니다. 프로세스 하나가 실행될 때마다 운영체제는 PCB를 두고 이 프로세스에 메모리를 얼마나 줘야할지 등을 관리합니다.
그래서 프로세스 문맥을 파악하기 위해서는 운영체제가 이 프로세스에 대해 어떤 값을 가지고 있는지도 알아야하기에 커널이 가지고 있는 프로세스의 PCB를 파악해야합니다.
또, 지난 포스팅에서 프로세스가 운영체제에서만 할 수 있는 일을 요청하는 것을 시스템 콜이라고 설명했습니다. 만약 프로세스에서 시스템 콜을 하게 되면 위 사진처럼 Program Counter가 자신의 주소 공간의 code를 가르키는 것이 아닌, 커널 주소 공간을 가르키며 커널의 코드를 실행할 것입니다. 그런데 이 커널도 함수들로 구성되어 있고, 이 커널에서 함수 호출이 이루어지면 스택에다가 관련 정보를 쌓아놓게 됩니다.
그런데 이 커널이라는 것은 여러 프로세스들이 공유하는 자원입니다. 그렇기에 어떤 프로세스든 운영체제에 요청하여 커널의 코드를 실행하게 되면 이 커널이 어떤 프로세스의 요청을 받고 실행하는지가 구분되어야 합니다. 그래서 프로세스 별로 커널의 스택을 별도로 두고 있습니다. 그래서 프로세스의 현재 상태를 규명하기 위해서는 특정 프로세스의 커널 스택도 어떤 내용을 쌓고 있는지 그 프로세스를 파악하기 위해 필요합니다.
위 설명들을 조금 더 쉽게 이해하기 위해서는 프로세스가 혼자 실행되는 것이 아닌 현대의 컴퓨터 시스템에서는 프로세스들이 번갈아가면서 실행이 된다는, 하나의 프로세스가 CPU를 잡고 실행을 하다가 또 CPU를 놓아주고 다른 프로세스가 CPU를 사용하는 멀티 태스킹이 동작한다는 사실을 이해해야 합니다.
그러니 특정 프로세스의 문맥을 알고 있지 않다면, CPU를 다른 프로세스에 넘겨주고 다시 잡았을 때, 그 프로세스의 앞 부분부터 다시 실행해야하는 문제가 생기는 것 입니다. 그래서 항상 프로세스의 문맥을 파악하고 있다가 그 시점부터 다음 인스트럭션을 실행할 수 있는 것입니다.
2. 프로세스의 상태와 상태도
프로세스의 상태는 다음으로 구분 됩니다.
- Running : CPU를 잡고 Instruction을 수행 중인 상태
- Ready : CPU를 기다리는 상태(메모리 등 다른 조건을 모두 만족)
- Blocked : CPU를 주어도 당장 instruction을 수행할 수 없는 상태,
Process 자신이 요청한 event(ex: I/O 작업)가 즉시 만족되지 않아 이를 기다리는 상태 - New : 프로세스가 생성 중인 상태
- Terminated : 수행이 끝난 상태 (조금은 정리할게 남아있는 상태)
대부분의 시스템은 CPU가 하나 밖에 없기 때문에 CPU를 잡고 있는 프로세스는 매 순간 하나일 것입니다.
CPU를 잡고 인스트럭션을 실행하고 있는 그 상태를 Running이라고 부릅니다.
그리고 CPU가 하나 밖에 없기 때문에 다른 프로세스들이 CPU를 쓰려고 기다리는 상태를 Ready라고 합니다. Ready 상태는 필요한 부분들은 이미 물리적인 메모리에 올라와있고, CPU를 얻는다면 당장 인스트럭션을 실행할 수 있는 상태입니다. 그래서 보통 레디 상태의 프로세스들이 번갈아가며 CPU를 잡았다가 놓는 식으로 타임 쉐어링을 구현하고 있습니다.
CPU를 얻어도 인스트럭션을 실행하지 못하는 상태가 있습니다. 오래 걸리는 I/O 작업을 한다던지, 디스크에서 무엇을 읽어와야 다음 인스트럭션을 실행할 수 있는 그런 프로세스들은 CPU를 얻더라도 당장 인스트럭션 수행이 불가합니다. 그런 상태를 Blocked (혹은 Wait, Sleep) 라고 합니다.
프로세스의 상태는 이렇게 크게는 세 가지(Running, Ready, Blocked)로 구분됩니다.
3. 프로세스를 실행하는 과정
CPU는 굉장히 속도가 빠르고 여럿이서 공유하는 자원이기 때문에 하나의 프로세스만이 접근할 수 있습니다(사진의 가운데 초록 점이 프로세스). 하나의 프로세스가 CPU에서 Running을 하고 있다가 타이머 인터럽트가 들어오면 CPU를 뺏기고 맨 뒤(queue)에 줄을 서고 그 다음 프로세스가 CPU를 얻고 이런 식으로 돌아가면서 CPU를 사용하게 되어있습니다.
그러던 중 CPU에서 실행되던 프로세스가 디스크에서 무언가를 읽어와야 하는 경우가 생기면 이 프로세스의 상태는 Running에서 Blocked로 바뀌면서 디스크의 Queue에 이 프로세스가 줄을 서게 됩니다. 그러면 디스크의 컨트롤러가 요청이 들어온 것들을 순서대로 처리하게 됩니다.
이후, 디스크에서 읽는 작업 등이 끝나게 되면 디스크 컨트롤러가 CPU에게 인터럽트를 걸게 됩니다. 그럼 요청한 I/O 작업이 끝났다는 것을 CPU에게 알려주게 되고 CPU는 어떤 프로세스를 실행하던 중이었지만, 인터럽트가 들어왔기 때문에 하던 작업을 멈추고 이 CPU의 제어가 운영체제 커널에 넘어가게 됩니다.
그럼 운영체제 커널은 해당 프로세스의 메모리 영역에 해당하는 데이터를 넘겨주고, 프로세스의 상태를 Blocked에서 Ready로 바꿔서 CPU를 당장 사용할 수 있는 상태를 만듭니다.
참고자료
[KOCW 이화여대 반효경 교수님 운영체제 - Process 1]
https://core.ewha.ac.kr/publicview/C0101020140318134023355997?vmode=f
[Operating System Concepts - Abraham Silberschatz]
https://www.yes24.com/Product/Goods/89496122