탐비의 개발 낙서장

[운영체제] 프로세스와 스레드 스케줄링 본문

프로그래밍/운영체제

[운영체제] 프로세스와 스레드 스케줄링

탐비_ 2021. 8. 2. 23:47
프로세스와 스레드

 

프로세스가 메모리에 적재될 때 구조

 

프로세스 Process - 작업의 단위

 

 CPU가 처리하는 작업(Task)라고도 불리며, 실행중인 프로그램을 의미합니다. 구체적으로는, 디스크에 저장되어 있던 실행 가능한 프로그램이 메모리에 적재되어 운영체제가 관리하는 상태를 의미합니다.

 

프로세스의 상태 변화

 

프로세스 상태 전이도

 

- New : 프로세스가 생성되기 위해 준비된 상태로 아직 디스크에 프로그램의 형태로 존재

- Ready : 메인 메모리에 적재 되어 프로세스에 의해 실행되기를 대기하는 상태. OS의 대기 큐에 적재됨.

- Running : 프로세스가 실행되는 중인 상태

- Waiting : I/O와 같은 이벤트로 인해 대기중인 상태

- Terminated : 프로세스가 실행 완료된 상태로, 프로세스 제어 블록(PCB)가 삭제됨.

 

스레드 Thread - 실행의 단위

 

 하나의 프로세스 내에서 실행되는 하나 또는 여러개의 작업 단위입니다. 프로세스가 할당받은 자원을 이용하는 실행의 단위이며, 프로세스의 메모리 구조에서 스택(독립적인 실행 가능) 및 레지스터(스케줄러에 의한 선점 시 기억)만 따로 할당받고 그 외 영역은 공유합니다.

 즉, 프로세스는 하나 이상의 스레드를 가지고 각 스레드는 역할에 따라 동작을 담당합니다.

 

 

 

멀티 프로세스 vs 멀티 스레드

 

멀티 프로세싱

 

 멀티 프로세스라고 하면 하나의 프로그램을 여러개의 프로세스로 구성하여 각 프로세스가 하나의 작업을 처리하도록 하는 것을 말합니다. 

 여러개의 자식 프로세스 중 하나에 문제가 발생하면 그 프로세스만 죽게되고 영향이 확산되지 않는 장점이 있지만, Context Switching에서의 오버헤드가 발생합니다. 이는 프로세스는 각각의 독립된 메모리 영역을 할당받아, 프로세스 간 공유하는 메모리가 없어 캐쉬 메모리 초기화 등의 무거운 작업이 진행되고, 많은 시간이 소모됩니다.

 

# Context Switching

 CPU에서 여러 프로세스를 돌아가면서 작업을 처리하는 과정. 동작 중인 프로세스가 대기를 하면서 해당 프로세스의 상태(Context)를 보관하고, 복구하는 작업

 

멀티 스레드

 

 하나의 응용프로그램을 여러 개의 스레드로 구성하고 각 스레드가 하나의 작업을 처리하도록 하는 방법입니다. 윈도우나 리눅스 등 많은 운영체제들이 멀티 스레딩을 기본으로 하고 있습니다.

 시스템 자원 소모 감소, 시스템 처리량 증가(빠른 Context Switching), 간단한 통신 방법등 다양한 장점이 있지만 주의 깊게 설계되어야하고, 디버깅이 까다로우며 자원 공유의 문제(동기화)가 발생합니다.

 

멀티 스레드를 사용하는 이유

 

1. 자원의 효율성 증대

 멀티 프로세스를 사용하게 되면 프로세스를 생성하여 자원을 할당하는 시스템 콜이 일어나게 됩니다. 구체적으로, 프로세스 간의 Context Switching시 CPU 레지스터 교체 뿐만 아니라 RAM과 CPU 사이의 캐쉬 메모리에 대한 데이터까지 초기화 되므로 오버헤드가 더욱 커집니다.

 멀티 스레드는 프로세스 내의 메모리를 공유하기 때문에 독립적인 프로세스와 달리, 스레드 간 데이터를 주고 받는 것이 간단해지고 시스템 자원 소모가 줄어들게 됩니다.

 

2. 처리 비용 감소 및 응답 시간 단축

 프로세스간의 통신(IPC - 파이프, 파일, 소켓 등을 이용)보다 스레드 간의 통신 비용이 적은데, 이는 스레드 간에는 Stack 영역을 제외한 모든 메모리를 공유하기 때문입니다.

 

동기화 문제 - Mutex & Semaphore

 

 스레드 간의 자원 공유는 전역 변수(데이터 세그먼트)를 이용하게 됩니다. 이를 이용하는 코드를 임계 구역(Critical Section)이라고 하는데, 공유하는 전역 변수를 여러 스레드가 함께 사용하려면 충돌하는 문제가 발생하므로 병행 처리를 위한 동기화를 통하여 문제를 해결해야합니다.

 

1. Semaphore

 세마포어는 리소스의 상태를 나타내는 카운터라고 할 수 있습니다. 세마포어는 P와 V라는 동작에 의해서 접근할 수 있는데, 현재 사용 가능한 자원인 S에 따라 동작합니다.

- P Operation : 임계 구역에 들어가기 전에 수행 => S != 0 -> S = S-1

                     보호되는 자원이 사용 가능할 때 까지 Wait / Sleep

- V Operation : 임계 구역에서 나올 때 수행 => S = S+1

 

2. Mutex(Mutual exclusion)

 S 값을 1로하는 세마포어와 같은 동작을 합니다. 임계 영역에 들어갈 때 락(Lock)을 걸어 다른 프로세스(or 스레드)가 접근하지 못하도록 하고, 임계영역에서 나와 해당 락을 해제(Unlock)합니다.

 

 

멀티 스레드 스케줄링

 

 스케줄링 Scheduling

 

 메모리에 적재된 프로그램을 CPU가 실행할 수 있도록, 운영체제로 하여금 프로세스 또는 스레드에 CPU를 할당하는 것을 의미합니다. 스케줄러는 제한된 자원을 여러 프로세스가 효율적으로 운영하도록 다양한 정책을 가지고 CPU를 할당하게 되는데, 여기서 정책이란 어떤 기준 / 순서로 CPU를 할당할지를 결정하는 방법입니다.

 

 스케줄러는 프로세스 선택 기준을 정하는 정책 부분과 선택된 프로세스에 CPU를 할당하는 디스패처(Dispatcher)로 구성되는데, 결국 프로세스 스케줄링은 준비 큐의 프로세스 중 정책에 따라 하나의 프로세스를 선택하여 CPU를 할당하는 행위를 말합니다.

 

 

스케줄링 방법

 

1. 비선점 스케줄링 : CPU를 선점한 프로세스가 자발적으로 실행권을 내려놓는 방식을 의미합니다.

 

- FCFS(First Come First Served) : 큐에 도착한 순서대로 CPU를 할당합니다. 짧은 작업이 긴 작업을 기다리게 되는 경우나 중요한 작업의 순위가 후순위로 밀리는 경우가 발생할 수 있습니다.

 

- SJF(Shortest Job First) : 실행시간이 짧은 프로세스에게 먼저 CPU를 할당합니다. 그러므로 실행시간이 긴 프로세스는 순위가 계속 밀려 기아상태가 발생하게 됩니다.

 

- 우선순위 : 우선순위가 높은 순으로 CPU를 할당합니다. 이 방법 역시 낮은 우선순위를 가진 프로세스가 기아 상태가 발생하게 됩니다.

 

- HRRN(Highest Response Ratio Next) : 준비 큐에 있는 프로세스들 중 응답률[(대기시간+CPU 요구량) / CPU 요구량]이 가장 높은 프로세스에게 높은 우선 순위를 주는 방식입니다. 대기시간이 길어질수록 우선순위가 높아지므로 수행시간이 긴 프로세스도 금방 CPU를 할당받을 수 있게 됩니다.

 

2. 선점 스케줄링 : 실행중인 프로세스가 강제적으로 실행권을 빼앗기는 방식을 의미합니다.

 

- Round-Robin : 모든 프로세스에 CPU 할당 시간을 부여하여 CPU의 사용 시간을 제한하는 방식입니다. 할당 시간이 너무 길면 FCFS와 같은 문제가 발생하고, 너무 짧으면 Context Switch에 의한 오버헤드가 발생합니다.

 

- 선점 우선순위 : 우선순위가 높은 순으로 번갈아가며 CPU를 할당합니다. 비선점 우선순위 방법과 다르게 Aging 진행에 따라 우선순위가 바뀌면 프로세스가 실행권을 잃고 더 높은 우선순위의 프로세스로 전환됩니다.

 

- 다단계 피드백 큐 : 프로세스를 우선순위에 따라 그룹화하고, 각자 다른 큐를 사용하는 방법입니다. 처음에는 모든 프로세스가 첫 번째 큐에 등록되고, 할당 시간 내에 종료되지 않을 경우 다음 차례의 프로세스에 선점되고 한 단계 낮은 우선 순위 큐에 등록됩니다. 즉, 많은 CPU 사용 시간을 요구하는 프로세스를 점점 낮은 우선순위로 배치하는 방법입니다. 이 방법 역시 Aging을 통해 기아 현상을 해결할 수 있습니다.

 

# Aging : 기아 상태가 발생하는 현상을 처리하는 방법으로, 실행되지 않은 프로세스의 우선순위를 시간이 지날수록 높여주는 방법입니다.

 

 

Thread Pool

 

 스레드도 결국 단순한 실행의 흐름이 아니라 메모리를 소유하고 있습니다. 프로세스에 메모리를 할당하고, 스레드에 메모리를 할당하는 작업은 상당히 비용이 큰 작업입니다. 결국 이 말은 스레드를 무제한으로 만들 수는 없다는 것을 의미하게됩니다. 그렇다면 프로세스가 많아질 때 어떻게 성능 향상을 할 수 있을까요?

 

 스레드 풀은 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업 큐에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 방식입니다. 프로세스가 많아지는 상황에서 모든 프로세스에 각각 스레드를 생성/수거하여 부담이 발생하게 된다면 프로그램의 전체적인 퍼포먼스에 영향을 주게됩니다.

 

스레드 풀 동작 원리

 

 위 그림처럼 우리가 만든 프로그램에서 사용자로부터 들어온 요청을 작업 큐에 넣게되고, Task를 미리 생성해 놓은 Thread들에게 할당합니다. 이렇게 작업을 하게되면 작업 처리 요청이 폭증하게 되어도 스레드의 전체 개수를 제한하여 하나씩 처리하는 방식이므로 시스템 성능이 급격하게 저하되는 것을 방지해줍니다.

 

 

References

프로세스와 스레드 https://gmlwjd9405.github.io/2018/09/14/process-vs-thread.html

세마포어와 뮤텍스 https://technote.kr/301

스레드 풀 https://limkydev.tistory.com/55

스케줄링 기법 https://woo-dev.tistory.com/148

HRRN 스케줄링 https://itdexter.tistory.com/393