기억을 더듬어서 CPU 스케쥴링이란건 뭘까? 내 기억으로는 락을 학습할때 락 해제를 도와주는 그런 역할로 나왔던걸로 기억한다.
그리고 프로세스 동기화 같은 경우 일반적으로 프로세스는 독립적인 공간이 존재하기 때문에 동기화는 불가능하다. 하지만 이것을 가능하게 해주는 것이 IPC라고 알고 있다. 물론 이러한 사실들이 잘못된 내용들이 많다고 생각한다. 방금 GPT를 돌려보니 대부분 내가 한말이 틀렸다고 한다. 그렇다면 다시 잡는게 좋지 않을까 생각이 든다. 듬성듬성한 기억을 더듬어서 프로세스부터 스레드까지 다시 한번더 정리해보자.
프로세스 상태
프로세스는 독립된 메모리 공간이라고 했습니다. 그리고 다음과 같은 상태가 있다고 했습니다. 프로세스가 새로 생긴 상태, 그 프로세스가 동작하는 상태, 프로세스가 죽은 좀비 상태, 프로세스가 완전히 없어진 상태 요렇게 학습을 했습니다.
대략적으로 다음과 같은 상태가 존재한다고 합니다. 제가 빼먹은거 부터 다시 작성하면 요렇습니다.
"생성 → 준비 → 실행 → 대기 ↔ 준비 → 종료 → 좀비 → 해제" 이라고 합니다.
이제 저러한 상태가 되어지면 프로세스는 실제로 어떻게 동작하는지 알아봅시다.
일단 커널은 해당 프로세스의 PID가 실제로 사용중인지 아닌지 알아야 합니다. (참고로 좀비 상태도 사용중입니다.)
이때 PID와 비트맵을 활용한다면, 0또는 1이라는 결과가 나오게 됩니다. 이는 마치 PC방에서 자리를 선점하는 거와 비슷하다고 보시면 됩니다.
그렇다면 커널은 어떻게 사용/미사용 여부를 판별할까요?
내부적으로는 비트 선택 알고리즘(Bitmasking)을 사용해 해당 비트 하나만 정확하게 추출합니다.
이 알고리즘의 자세한 내용은 추후 별도로 학습할 계획이며, 그때 더 깊게 파볼 예정입니다.
이렇게 함으로써 커널은 특정 PID가 현재 사용 중인지, 혹은 미사용 상태인지 정확하게 판별할 수 있게 됩니다.
만약, 해당 PID가 미 사용인경우 커널은 생성 명령어를 통해 프로세스를 생성하게 될겁니다.
요 부분을 자세하게 살펴봅시다.
생성
가장 기본적인 뼈대를 생각해보면 다음과 같습니다.
프로세스는 PID가 배정되고, 내부 구조(PCB,스택,메모리)가 모두 만들어진 뒤, 마지막으로 스케줄러에 등록되어야
'실행 가능한 프로세스'로 최종 완성되어집니다.
이것을 요렇게 비유할 수 있을 거 같습니다.프로세스를 "학교"라고 하고, 학교가 수학여행을 준비하는 과정이라고 가정해봅시다.
수학여행을 가기로 결정하는건 마치 새로운 프로세스를 만들라는 요청이 들어온 상황입니다.
CPU에는 유저 공간과 커널 공간으로 나눠져 있습니다.

이것을 비유한대로 이해하면, 학교측에서 수학여행을 가기위해 수련원에 요청을 보내는거와 비슷하다고 생각하시면 됩니다.
학교자체에서 수학여행을 갈 수 없습니다. 이것을 관리하는 곳에 허락을 맡아야 갈 수가 있죠.
대신 학교측은 관리하는 곳 즉, 수련원에 수학여행을 갈거라는 요청 메시지는 보낼 수는 있습니다.
프로세스 생성도 이와 비슷하게 동작합니다.
이제 주체는 유저 측이 아니라 커널이 모든 일을 처리하는 단계로 넘어오게 됩니다.
다만 이 시점에서도 프로세스는 아직 "생성 완료"가 아닙니다. 단지 생성 요청만 들어온 상태일 뿐이죠.
여기서 한 가지 문제를 생각해봅시다. 만약 수련원에 수학여행을 오는 학교가 한 곳이 아니라 수십, 수백 곳이라면 어떻게 구분해야 할까요?
처음에는 학교 이름으로 분류할 수 있겠지만, 10개 정도는 몰라도 100개 이상이 되면 이름 기반 관리 자체가 불가능해집니다.
그래서 수련원은 결국 모든 학교에 번호를 부여하는 방식을 택합니다. 번호를 통해 빠르고 일관된 관리가 가능해지기 때문이죠.
하지만 번호만 있다고 끝나는 건 아닙니다. 해당 번호에 실제 학교가 존재하는지, 비어 있는지 확인할 수 있어야 합니다.
소규모라면 "없는 번호는 바로 빈 자리"라고 판단할 수 있지만, 프로세스가 수천·수만 단위로 증가하는 경우 컴퓨터는 1번부터 끝까지 전부 조회해야 존재 여부를 알 수 있습니다. 이 방식은 당연히 심각한 성능 저하를 유발합니다.
OS는 기본적으로 최고의 성능을 유지해야 합니다. 그런데 PID 전체를 처음부터 끝까지 스캔해야 한다면 절대 효율을 낼 수 없겠죠.
그래서 등장하는 것이 바로 비트맵 알고리즘입니다. 앞에서 언급했듯이, 비트맵을 사용하면 특정 번호(PID)가 사용 중인지 아닌지 즉시 파악할 수 있습니다.이 방식이 대규모 프로세스 환경에서도 빠르게 빈 PID를 찾아낼 수 있는 핵심 메커니즘입니다.
비트맵 알고리즘을 통해 빈 번호(PID)를 빠르게 확인할 수 있게 되면, 이제 프로세스를 만들기 위한 '서류 제출' 단계가 진행됩니다.
유저 공간이 커널에 프로세스 생성 요청을 제출하는 단계라고 보면 됩니다.
커널은 이 요청을 바탕으로 내부 구조(PCB, 커널 스택, 메모리 매핑 등)를 생성하고 프로세스의 속성을 설정하는 단계로 넘어가게 됩니다.
학교측 비유로 들면,수학여행 신청서(서류)에는 기본 일정이나 숙박 정보 같은 공통적인 구조가 담겨 있을 것입니다.
여기에 더해, 각 학교마다 준비한 특별 활동이나 요구사항 같은 고유 정보도 함께 기재되어 제출되겠죠.
모든 서류 검토가 끝나면, 해당 학교는 수학여행 일정표의 출발 대기 칸에 등록됩니다.
이는 운영체제에서 프로세스가 Ready Queue에 올라가 실행 차례를 기다리는 상태와 같습니다.
Ready Queue: 여기에 등록된 프로세스들은 스케줄러가 CPU에 올리기 전까지 대기하는 상태라고 보면 된다. 또한 자료구조의 queue와 달리 FIFO는 아니다.
준비
Ready Queue에 준비 상태인 프로스세는 실행하기전 어떤 일을 할까요? 애석하게도 아무일도 하지 않는다고 합니다.
마치 주차장에 대기하고 있는 차량 같다는 생각이 듭니다. 그리고 나서 스케쥴러가 프로세스를 실행한다고합니다.
그렇다면, 의문이 듭니다. 언제까지 준비상태에 있을까요? 평생동안 준비만 하지는 않을테니 말이죠.
일반적으로 Ready 상태에서의 진행 방향은 크게 세 가지입니다.
첫 번째, 스케줄러가 CPU를 배정하면 Ready → Running 상태로 전이됩니다.
두 번째, CPU 우선순위가 낮거나 스케줄링 정책에 의해 뒤로 밀리는 경우, Ready 상태가 일정 시간 동안 유지될 수 있습니다. 즉, 실행이 늦춰지는 경우입니다.
세 번째, 실행되기 전에 프로세스가 종료되는 경우입니다. 이때는 Running으로 가지 못한 채 Ready 상태에서 바로 종료(Exit)로 넘어가며, 곧바로 해제 과정으로 이어집니다.
그렇다면 영원히 준비 상태에 돌입이 되는 경우는 없을까요? 우선순위가 너무 낮아서 실행이 안되는 경우 어떻게 처리가 되어질까요?
현대 OS는 다음 3가지 방법으로 이 문제를 해결한다고 합니다. 그래서 현대 OS는 영원히 프로세스가 준비상태인 경우는 없다고 합니다.
- aging 기법 (시간이 지나면 우선순위를 점점 올림) (윈도우 방식)
- Completely Fair Scheduler (리눅스 방식)
- 타임슬라이스 균등 배분
핵심적인 방법은 저 3가지라고 하는데 오해하면 안 되는게 저 3가지는 동시에 발생이 되지 않습니다. 그 예시로 리눅스에서는 aging방법을 채택하지 않는다고 합니다.
Aging/CFS = "누가 먼저 CPU를 받을지(우선순위 선택)"를 결정하는 기술
타임슬라이스 균등 배분 = "CPU를 얼마나 오래 사용할지(시간 독점 방지)"를 조절하는 기술
이에 대한 이야기는 추후 다시 다뤄보죠. (아직 100%이해가 된건 아니지만 조금씩 학습해봅시다.)
실행
이제 준비상태에서 우선순위에 의해 결정된 프로세스들이 하나 둘씩 실행이 되어집니다. 여기서 스케쥴링이 들어가게 되어집니다. 일반적으로 스케쥴링을 생각할때 스케쥴에 맞춰서 동작한다고 생각할 수 있습니다. 과연 여기에서 말하는 스케쥴링이 우리가 아는 스케쥴링과 같을까요? 여기서 말하는 스케쥴링은 스케쥴에 초점을 맞추는 것이 아닌 순서에 초점을 맞춰야 합니다. 즉, 어떤 방법으로 순서를 정해서 실행을 시킬지 생각해야 한다는 거죠.
어떤 방법으로 순서를 정할 수 있을지 생각해봅시다.
스케쥴링
총 5가지 방법이 존재한다고 한다. FCFS, 우선순위, RR, SFJ, CFS요렇게 있다.
간단하게 살펴봅시다.
- FCFS: 먼저 들어온 것 부터 처리하는 방식
- RR: 균등하게 돌아가면서 처리하는 방식
- 우선순위: 우선순위를 지정해서 우선순위가 가장 높은것 부터 처리하는 방식
- CFS: CPU가 가장 적게 쓸거 같은거 부터 처리하는 방식
- SFJ: 짧게 동작할 거 같은 것 부터 처리하는 방식
대표적으로 이렇게 5가지가 있다고 합니다.
FCFS부터 생각해봅시다. 먼저 들어온 친구를 먼저 해결합니다. 어떻게 보면 가장 합리적이라고 생각이 들 수 있습니다. 하지만 여러 프로세스가 동작한다면 상황은 달라집니다. 프로세스A와 프로세스B가 레디 큐에 저장이 되었다고 생각해봅시다. A가 레디큐에 먼저 저장이 되었다면 A부터 실행이 되어질겁니다. 하지만 B는 A가 종료된 이후에 B가 실행이 되어집니다. 만약, A가 실행 시간이 길면 어떻게 될까요? B는 A가 종료 될때까지 영원히 기다려야 할겁니다. 즉, 멀티 태스킹이 안된다고 불수 있죠. 다른 말로 호위 효과라고 부른다고 합니다.
이런 구조 때문에 FCFS는 "긴 작업 하나가 전체 시스템의 처리 속도를 끌어내리는 방식"으로 평가받습니다.
즉, 손쉽고 단순하다는 장점이 있지만, 실제 OS에서는 거의 사용되지 않는 비효율적인 스케줄링 방식입니다.
이것을 해결하기 위해 RR과 우선순위가 등장하게 되었습니다.
먼저 RR부터 얘기해보죠.
RR은 프로세스에게 동일한 CPU 실행시간(타임슬라이스)을 부여해 순차적으로 돌아가며 실행시키는 방식입니다. 여기서
그렇다면 최초의 프로세스는 어떻게 할 수 있을지 궁금할겁니다. 그거 같은경우는 FCFS와 똑같은 방법으로 동작한다고 합니다.
실행 시간을 균등하게 가져가기 때문에 멀티 테스킹을 지원할 수 있게 되었습니다. 하지만 언제 종료할지 모릅니다.
하지만 단점도 존재합니다. 타임슬라이스가 짧을수록 프로세스 간 전환이 매우 자주 발생하므로, 컨텍스트 스위칭 비용이 급증하게 됩니다.
결국 "모든 프로세스를 골고루 실행시키는 공정성"을 얻는 대신, 성능 저하라는 비용을 치르게 되는 셈입니다.
그다음 우선순위입니다. 우선순위는 말 그대로 프로세스마다 우선순위를 지정해서 동작시키는 방식을 말합니다. 우선순위를 어떻게 지정할 수 있을지를 고민하기 이전에 이게 FCFS의 호위 효과를 어떻게 해결했을까요? 우선순위를 부여함으로써 짧은 작업(또는 중요한 작업)을 먼저 실행할 가능성이 높아집니다. 하지만 우선순위가 있다고 해서 FCFS의 문제를 완벽하게 해결하는 것은 아닙니다.
OS는 프로세스가 얼마나 빨리 끝날지 정확히 알 수 없기 때문에, 우선순위를 잘못 부여하면 오히려 긴 작업이 먼저 실행되거나,
짧은 작업이 뒤로 밀리는 문제가 다시 발생할 수 있습니다. 또한 우선순위가 낮은 프로세스는 영원히 실행되지 않는 기아(starvation) 문제도 발생하게 됩니다. 그래서 우선순위 스케줄링은 호위 효과를 일부 완화할 수 있지만, 완벽한 해결책은 아닙니다.
트레이드 오프를 해야할때입니다. 문제를 확실하게 해결하는 RR을 선택할 것인가, 문제를 완화하지만 부작용을 남기는 우선순위를 선택할 것인가, 아니면 아예 새로운 접근을 선택할 것인가?둘다 아니다 새로운 새로운 접근을 선택할 것인가?
참고로 window는 우선순위와 RR을 혼합한 방식을 사용한다고 합니다. 그냥 RR이 아니기 때문에 기본 RR처럼 컨텍스트 스위칭가 많이 발생하지는 않지만 공정성을 기반으로 설계된 Linux의 CFS보다는 스위칭이 더 빈번하게 발생합니다.
위에서 잠깐 언급한 CFS부터 알아볼까요? 리눅스는 윈도우와 달리 다른 스케쥴러를 선택하게 되었습니다. 지금까지 살펴본 이슈들을 어떻게 해결했을까요?
CFS는 "실제 사용한 CPU 시간"을 기준으로 순서를 정합니다. CPU를 많이 사용한 프로세스는 잠시 뒤로 밀리고,
CPU를 적게 사용한 프로세스는 앞으로 당겨져 먼저 실행됩니다. 이것이 공정성을 위한 CFS의 핵심 메커니즘입니다. 이것 또한 컨텍스트 스위칭이 많이 발생하겠지만, RR은 타임슬라이스 기준으로 무조건 잘라버리는 방식이고, CFS는 실행 흐름을 조절하면서 필요한 시점에만 교체하기 때문에 불필요한 컨텍스트 스위칭이 크게 줄어드는 것이죠.
CFS는 중요하게 빠르게 처리될 일은 중요하게 생각하지 않습니다. 오로지 시간량에 따라 정해지기 때문에 전체적인 처리량은 다소 느려질 수 있습니다.
다음과 같은 단점들이 있다고 합니다. (제가 설명을 못할거 같아 표로 대체합니다.)
| 구분 | 단점 | 설명 왜 문제가 되는가? | 실제 영향 사례 |
| 단점 1 | 중요 작업(High Priority Task)을 빠르게 끝내지 못한다 | CFS는 우선순위(priority)를 절대 기준으로 사용하지 않음. CPU 사용량이 적은 프로세스를 먼저 실행하므로 중요한 작업이 즉시 실행되지 않을 수 있음 | 실시간 처리, 금융 트랜잭션, 긴급 이벤트 처리에서 응답 지연 발생 |
| 단점 2 | 전체 처리량(Throughput)이 감소할 수 있다 | 긴 작업을 몰아줘야 처리량이 극대화되는데, CFS는 긴 작업을 뒤로 보내고 짧은 작업을 끼워넣어 공정성을 유지하려 하기 때문 | 대규모 일괄 처리(batch system)나 고성능 처리 시스템에서 총 처리 시간 증가 |
| 단점 3 | CPU-bound 작업이 많은 환경에서 성능 저하 | CPU를 많이 쓰는 프로세스를 계속 뒤로 밀어버리므로, CPU 집중 작업이 빨리 끝나지 않음 | 머신러닝 연산, 렌더링, 컴파일, 빅데이터 분석 작업 시간이 더 늘어날 수 있음 |
* 이에 대한 자세한 내용은 담지 않았습니다. ex) CPU-bound가 먼지... , 리눅스가 왜 CFS를 선택했는지... window와 리눅스의 차이 등등
사실 이론상으로는 완벽한 스케쥴러가 있습니다. 그건 SFJ인데요. 이게 왜 이론상 완벽한지 왜 사용하지 않는지에 대해 알아봅시다.
SFJ는 가장 사용하지 않을거 같은 잡을 미리 예측해서 프로세스를 관리하는 스케쥴러입니다. 만약, 예측만 한다고 하면, 좋겠지만 현실적으로 불가능합니다. 가능한다고 해도 예측하는 연산을 해야 하기때문에 성능적으로도 좋다고 평가할 수 는 없습니다.
이렇게 5가지의 스케쥴러를 알아봤습니다. 물론 스케줄러는 지금 설명한 5가지말고 더 많습니다. 예를 들면 window에 특화된 스케쥴러라던지 mac OS에 특화된 스케쥴러라던지... 하지만 모든 스케쥴러는 FCFS를 기반으로 동작이 된다는 사실입니다. 그리고 한 가지 목표를 위해 RR이 도입이 되죠. 그 목표는 멀티 테스킹입니다. 그리고 나머지는 시간을 균등하게 가져가냐 평등하게 가져가냐에 따라 여러가지 스케쥴러가 나타납니다. 현대에는 어떤 스케쥴러가 어울릴까요?
종료&좀비&해제
레디 큐에서 여러 프로세스들이 스케줄러에 의해 차례대로 실행 과정을 거칩니다. 하지만 이 모든 프로세스가 영원히 메모리 위에 존재하는 것은 아닙니다. 언젠가는 실행을 마치고 종료(Exit) 상태로 진입하게 되죠.
다만, 종료했다고 해서 즉시 사라지는 것은 아닙니다. OS 관점에서 프로세스는 먼저 좀비(Zombie) 상태를 거칩니다.
이 단계에서는 실행은 끝났지만 PCB(프로세스 제어 블록)만 잠시 남아 있는 상태입니다.
부모 프로세스가 이 정보를 회수하면(Wait) 그제서야 완전히 메모리에서 제거됩니다.
새 컴퓨터를 구입해 정상적으로 종료한 상태가 Exit, 종료는 했지만 처리가 남아 방치된 상태가 Zombie,
그리고 완전히 치워지고 소유권이 사라지는 상태가 해제(Final Removal)라고 보면 이해하기 쉽습니다.
프로세스 전이 순서는 생성 -> 준비 -> 실행 -> 대기 -> 종료 -> 좀비 -> 해제 입니다.
결론
프로세스가 어떤 흐름을 가지는지에 대해 학습해봤습니다. 더 나아가 스케쥴러에 대해 학습해봤습니다. 스케쥴러가 우리가 일상적으로 아는 스케쥴과 연관이 있을 줄 알았는데 전혀 아닌점이 신기했습니다. 어떤 순서로 프로세스를 실행을 시키는지에 대해 학습해보니 프로세스를 보는 시야가 조금더 달라지는 것 같다는 생각이 드네요.
시간이 된다면, 브라우저에 대해 면밀히 공부해보고 싶네요. 긴 글 읽어주셔서 감사합니다.!!!
'OS' 카테고리의 다른 글
| [OS] 네트워크 복습 + 프로세스와 스레드 (0) | 2025.11.09 |
|---|