[OS]Process Concepts

Aiden·2022년 9월 4일
0
post-thumbnail

PythonSingle Thread 에서 동작하는 이유에 대해 알아보던 중에 GIL(Global Interpreter Lock) 에 대해 알게 되었고, 이참에 ProcessThread 를 다시 정리해보면 좋을 것 같아 작성하게 된 글이다.


Process

📌 Process
A process is the instance of a computer program that is being executed by one or many threads.
Process(Computing) - Wikipedia

프로세스는 하나 혹은 다수의 스레드에 의해 실행중인 프로그램 인스턴스 를 의미한다.

즉, 프로그래밍 언어로 작성되어 디스크에 저장된 프로그램이 실행되고, CPU 자원을 할당받을 준비가 되었을 때, 프로세스라는 작업 단위로 불리는 것이다.


✅ 프로세스가 할당받은 메모리

위에서 CPU 자원을 할당받는다는 의미는 프로그램이 RAM에 올려져 독립된 메모리 자원을 할당받는다는 것을 의미하며, 이는 OS에 의해 이루어진다.

그렇다면, 할당받은 메모리 구조는 어떻게 이루어져있을까.

위 그림은 각각의 프로세스가 OS로부터 할당받은 메모리의 구조를 나타내고 있으며, 크게 Stack, Heap, Data, Code 로 나눌 수 있다.

💡 Code

설명 상 편의를 위해 Code 부터 설명하자면, Code 에는 우리가 작성한 코드가 저장된다. 더욱 정확하게는 프로그램 실행 도중 변경될 일이 없는 상수, 조건문, 반복문 등의 코드들이 컴파일 과정에서 기계어로 변환되어 Read-Only 로 저장되어 있다고 볼 수 있다.
따라서, 당연하게도 Code 에 저장되어 있는 내용들은 읽기 전용이므로 런타임에 수정 및 제거가 불가능하다.

💡 Data

Data 에는 전역변수(Global), 정적변수(Static), 구조체(Structure type) 와 같이, 프로그램의 시작시점부터 종료시점까지 유지되어야 하는 값들이 저장된다. 다만 Code 에 저장되는 값들과는 달리, 프로그램 실행 도중 변경될 가능성이 있기 때문에 미리 기계어로 변환되어 저장되지는 않는다.

💡 Heap

Heap 은 프로그래머에 의해 의도적으로 할당되는 값들이 저장되는 영역으로, 프로그램 실행 도중 malloc() 혹은 free() 와 같은 의도적으로 작성된 코드에 의해 할당 혹은 해제된다. 즉, 런타임에 동적으로 메모리가 할당되는 동적 영역이다.

💡 Stack

마지막으로, Stack 에는 지역변수, 리턴값 등 특정 함수 및 구문에서 사용되는 일반적인 매개변수 들이 저장된다. 따라서, 컴파일 시 메모리가 정적으로 할당되지만, 이는 해당 함수가 종료되면 자동으로 Stack에서 해제되는 값들이므로 동적으로 그 크기가 변하게 된다.

📌 정적 할당 vs 동적 할당
추가적으로, StackHeap 메모리는 동적 영역, DataCode 메모리는 정적 영역 으로 분류된다. 하지만, 이를 정적 할당, 동적 할당 과 헷갈려서는 안된다.
예를 들어, StackHeap 과 함께 동적 영역으로 분류되지만, Stack 에 저장되는 지역 변수 혹은 매개 변수는 컴파일 과정에 정적으로 메모리를 할당받기 때문이다.
반면, Heap 은 런타임에 동적으로 메모리를 할당받으면서, 동시에 동적 영역 으로 분류된다.


✅ PCB

프로세스가 생성되면서 PCB(Process Control Block) 라는 자료구조가 OS 의 커널에 저장된다. 이는 운영체제가 프로세스를 표현하는 방법이며, 내부의 정보(PID)를 활용해 각각의 프로세스를 식별 및 제어하게 된다.
특히, OS 의 Context Switching(문맥 교환) 이나 Process Management(프로세스 상태 관리) 를 위해 필수적으로 필요한 정보들을 담고 있다.

위 그림은 PCB 의 구조를 나타낸 그림이며, 이외에도 여러가지 정보가 담겨 있지만 여기서는 Pointer, State, PID, Program Counter 대해서만 간단하게 살펴보도록 하자.

💡 Pointer

먼저, Pointer부모 프로세스와 자식 프로세스의 주소, 현재 프로세스의 주소, 프로세스에 할당된 메모리의 주소 등을 저장하고 있다.
하나의 프로세스는 새로운 프로세스를 생성할 수 있는데, 이 때 생성되는 프로세스를 자식 프로세스, 기존의 프로세스를 부모 프로세스라고 한다.

💡 Process State

Process State 는 현재 프로세스의 상태를 의미하며, New(생성), Ready(준비), Running(실행), Waiting(대기), Terminated(완료) 의 다섯 가지 상태로 분류된다.

  • New: Process 가 생성되어 아직 메모리가 할당되지 않은 상태
  • Ready: Ready Queue 에서 CPU 할당을 기다리고 있는 상태
  • Running: CPU 를 할당받아 실행 중인 상태
  • Waiting: I/O 혹은 특정 Event 로 인해 대기 중인 상태
  • Terminated: Process 의 모든 작업이 완료되어 CPU 를 반납한 상태

추가적으로, 생성된 프로세스는 종료되기 전까지 Ready, Running, Waiting 의 단계를 반복적으로 거칠 수 있고 각각의 상태 전이는 아래와 같다.

  • Dispatch (Ready -> Running): CPU 를 할당받아 Running 으로 전이
  • Timer run out (Running -> Ready): 할당된 시간이 지나 Ready 로 전이
  • Wake up (Waiting -> Ready): I/O 작업, 이벤트가 종료되어 다시 Ready 로 전이

💡 PID

PIDProcess Identifier 로서, OS 가 각각의 프로세스를 구별 및 식별하는 데 사용되는 고유 번호이며, 이를 통해 직접 프로세스를 제어할 수도 있다.

💡 Program Counter

마지막으로, Program Counter 는 프로그램 계수기, 명령어 포인터라고도 불린다.
이는 Program Counter 가 다음에 실행될 명령어의 주소를 가지고 있어 실행할 기계어 코드의 위치를 지정하기 때문이다.
이 정보를 통해, Process 는 Context Switching 이 발생하더라도 이전의 작업 내용을 잃지 않고 이어서 실행할 수 있게 된다.

이외에도 PCBPriority, Memory Management Info, Accounting Info, I/O Status Info 등 Process 에 대한 다양한 정보들을 담고 있다.


✅ Context Switching

앞서, Process 의 상태 전이에 대해 살펴보면서 Ready, Running, Waiting 의 세 가지 상태를 반복적으로 가질 수 있다고 설명하였다.
이는 Process 가 생성되어 CPU 를 할당받아 실행되고 있는 도중이라도, 할당된 시간 초과, I/O 작업, 특정 Event 발생 등의 이유로 CPU 를 반납하고 대기하는 경우가 있다는 의미이다.

그렇다면, CPU 가 Process 를 교체하는 이유는 무엇일까?

기본적으로 하나의 CPU 는 하나의 Process 작업만을 수행할 수 있으며, 사용자는 컴퓨터에서 다양한 프로그램을 동시에 사용하게 된다.
(사용하고 있는 프로그램이 없더라도 기본적으로 시스템의 유지를 위해 실행되고 있는 프로세스가 굉장히 많다.)

따라서 일반적인 프로그램을 CPU 연산 작업과 I/O 작업의 반복이라고 볼 때, 프로그램이 I/O 작업 중일 때도 CPU 를 할당하는 것은 비효율적인 방식인 것이다.
(사용자 로그인을 위한 ID 와 Password 입력을 대기하고 있을 때 혹은 문서를 프린터로 출력하는 중일 때, CPU 가 할당되어 있는 것은 비효율적이다.)

위와 같은 이유로, 실행 중인 Process 는 특정 상황에서 할당받은 CPU 를 반납하고 Ready 혹은 Waiting 상태로 전이되며, 새로운 Process 가 CPU 를 차지하고 Running 된다.
바로 이렇게, 기존의 Process 에서 새로운 Process 로 CPU 가 새롭게 할당되는 작업을 Context Switching, 문맥 교환 이라고 부른다.

💡 과정

Context Switching 과정을 간단히 살펴보면 아래와 같다.

  • I/O, System Call, Event 등에 의한 Interrupt 발생
  • 기존 Process 사용자 모드 -> 커널 모드 전환
  • 기존 Process 상태 PCB 에 저장
  • 새로운 Process PCB 정보를 H/W 에 복구
  • 새로운 Process 커널 모드 -> 사용자 모드 전환

먼저, Timer run out 이나 I/O 작업, System Call 등의 사유로 문맥 교환의 조건이 만들어지면, 실행 중인 Process 는 CPU 스케줄러에 의해 Interrupt 가 발생하게 된다.

이후, 기존의 Process 는 실행 중인 사용자 모드에서 커널 모드로 전환되며, 전이된 새로운 상태, 지금까지의 작업 내용, 다음 번에 실행될 명령어의 주소 등의 정보를 PCB 에 저장한다.

CPU 스케줄러에 의해 선택된 새로운 프로세스는 자신의 PCB 에서 다양한 정보를 불러와 메모리와 같은 H/W 에 복구한다. 이 때, Program Counter 에서 다음 번에 실행될 명령어의 주소를 불러와 복구하고, Pointer 나 레지스터에서 Stack 의 메모리 주소 등을 저장하고 있기 때문에 사용자는 이전의 작업 내용을 잃지 않고 이어서 작업할 수 있다.

마지막으로, 새로운 Process 는 기존의 커널 모드에서 사용자 모드로 전환된다.

이러한 문맥 교환의 과정Process 의 상태가 전이될 때 발생하게 되며, 이 과정에서 사용되는 시간 및 메모리 등의 리소스는 시스템 상의 Overhead 이기 때문에 문맥 교환이 빈번하게 일어날 경우, Overhead 가 커지는 문제가 발생할 수 있어 주의해야 한다.

추가적으로, 다음에 실행될 Process 를 선택하는 기법을 Scheduling, 스케줄링 이라 부르며, 여기에는 다양한 스케줄링 알고리즘이 존재한다.
스케줄링에 대해서는 이후 별도의 포스팅으로 정리를 할 예정이다.


이렇게, Process 의 개념부터 Process 가 할당받는 가상메모리의 구조, PCB, Context Switching 까지, Process 의 핵심적이고 기본적인 내용들을 정리해보았다.

이후에는 Thread 에 대해 정리해보면서, GIL(Global Interpreter Lock) 을 통해 Python 이 Single Thread 에서 동작하는 이유에 대해서까지 포스팅할 계획이다.


Reference

0개의 댓글