88. 준비 큐 (Ready Queue)
핵심 인사이트 (3줄 요약)
- 본질: 준비 큐 (Ready Queue)는 주 메모리에 적재되어 CPU (Central Processing Unit) 할당을 기다리는 프로세스들의 제어 블록인 PCB (Process Control Block)를 연결한 커널 자료구조이다.
- 가치: 스케줄러 (Scheduler)의 정책에 따라 어떤 프로세스가 다음으로 실행될지 결정하는 핵심 대기열로, 시스템의 응답성과 처리량 (Throughput)을 직접적으로 좌우한다.
- 융합: 현대 운영체제는 단일 큐 대신 다단계 피드백 큐 (MLFQ)나 멀티 코어 스케줄링을 위한 코어별 런큐 (Runqueue) 등을 사용하여 락 경합 오버헤드를 최소화하고 있다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
- 개념: 준비 큐 (Ready Queue)는 프로세스가 실행에 필요한 모든 자원(메모리 등)을 확보하고, 오직 CPU (Central Processing Unit)의 할당만을 기다리는 상태(Ready State)에 있는 프로세스들의 대기열이다. 실제로는 프로세스 전체가 아닌 커널 메모 내의 PCB (Process Control Block)들이 포인터로 연결된 형태를 가진다.
- 필요성: 멀티프로그래밍 (Multiprogramming) 환경에서는 제한된 CPU 자원을 다수의 프로세스가 공유해야 한다. 수많은 프로세스 중에서 누구에게 언제 CPU를 넘겨줄지 체계적으로 관리하지 않으면, 자원 점유의 무질서와 기아 현상 (Starvation)이 발생한다. 준비 큐는 스케줄러가 공정하고 효율적인 결정을 내릴 수 있도록 돕는 필수적인 통제 기반이다.
- 💡 비유: 준비 큐는 "놀이공원 인기 놀이기구의 대기줄"과 같다. 입장권(메모리)은 이미 다 구매했고, 오직 롤러코스터(CPU)의 빈자리가 나기만을 줄 서서 기다리는 상태이다.
- 등장 배경:
- 배치 처리의 한계: 초기 일괄 처리 시스템에서는 한 프로세스가 끝날 때까지 CPU를 독점했으나, I/O 대기 시간 동안 CPU가 유휴 상태로 방치되는 비효율이 존재했다.
- 시분할 시스템의 도입: 타이머 인터럽트 (Timer Interrupt)를 통해 여러 프로세스가 CPU를 짧은 시간 동안 번갈아 사용하게 되면서, 실행 대기 중인 프로세스들을 효율적으로 보관할 자료구조가 필요해졌다.
- 다단계 스케줄링으로 진화: 단순한 FIFO (First-In First-Out) 방식에서 벗어나 우선순위 (Priority) 기반의 복잡한 스케줄링을 지원하기 위해 준비 큐 역시 다단계 배열 구조로 발전했다.
운영체제의 상태 전이 모델에서 준비 큐가 어떤 위치에 있는지, 그리고 프로세스가 어떤 이벤트를 통해 준비 큐로 진입하고 빠져나가는지를 보여주는 도식이다. 이 흐름을 이해하는 것은 스케줄링의 트리거 시점을 파악하는 데 필수적이다.
┌───────────────────────────────────────────────────────────────────────┐
│ 프로세스 상태 전이와 준비 큐의 위치 │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ [New] ───admit──▶ [Ready Queue] ◀───interrupt── [Running] │
│ │ │ │
│ │ I/O or │
│ dispatch Event │
│ │ │ │
│ ▼ ▼ │
│ [CPU 할당됨] [Wait Queue] │
│ │
│ 핵심: 준비 큐는 메모리에 적재되어 실행 가능한 상태의 프로세스 집합 │
└───────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이 도식에서 핵심은 프로세스가 준비 큐 (Ready Queue)로 진입하는 세 가지 경로다. 첫째, 새로 생성된 프로세스가 메모리를 할당받고 진입(admit)한다. 둘째, 실행 중(Running)이던 프로세스가 타임 슬라이스(Time Slice)를 소진하여 타이머 인터럽트(interrupt)에 의해 강제로 쫓겨난다. 셋째, 대기 큐(Wait Queue)에서 I/O 작업을 마친 프로세스가 깨어나(wakeup) 다시 진입한다. 따라서 스케줄러는 오직 이 준비 큐 내에 있는 대상들만 평가하여 다음 실행 프로세스를 디스패치(dispatch)하며, 이 큐의 병목이 곧 시스템 전체의 응답 지연으로 이어진다.
- 📢 섹션 요약 비유: 오케스트라 단원들이 무대 뒤에서 악기를 조율한 채 지휘자의 손짓(디스패치)만을 기다리며 일렬로 대기하는 공간과 같습니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
구성 요소
| 요소명 | 역할 | 내부 동작 | 프로토콜/알고리즘 | 비유 |
|---|---|---|---|---|
| 큐 헤더 (Queue Header) | 준비 큐의 시작점과 끝점을 관리 | Head/Tail 포인터를 유지 | FIFO (First-In First-Out) 연결 | 대기줄 안내 표지판 |
| PCB (Process Control Block) | 프로세스의 상태와 스케줄링 메타데이터 저장 | 큐 내의 노드로써 포인터로 상호 연결 | 커널 자료구조 | 탑승자 신원 확인증 |
| 스케줄러 (Scheduler) | 다음 실행할 프로세스 선택 알고리즘 | PCB의 우선순위, 대기시간 등을 평가 | RR (Round Robin), MLFQ | 놀이기구 탑승 관리자 |
| 디스패처 (Dispatcher) | 선택된 프로세스에 CPU를 실질적 할당 | 문맥 교환 (Context Switch) 수행 | 어셈블리 레벨 레지스터 복원 | 실제 좌석 안내요원 |
| 우선순위 배열 (Priority Array) | 우선순위별로 다수의 준비 큐를 운영 | O(1) 시간에 최고 우선순위 큐 검색 | 비트맵 (Bitmap) 연산 | VIP 전용 패스트트랙 |
준비 큐의 연결 리스트 아키텍처
준비 큐는 실제로 커널 공간 내에서 PCB 구조체들의 포인터 연결로 구현된다. 이 구조는 스케줄링 정책에 따라 노드 삽입 및 삭제 위치가 달라지며, 잦은 상태 전이에도 오버헤드를 최소화하도록 설계되어야 한다.
┌───────────────────────────────────────────────────────────────────────┐
│ 준비 큐(Ready Queue)의 내부 연결 구조 │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ [Ready Queue Header] │
│ ├── Head Pointer ────────┐ │
│ └── Tail Pointer ──┐ │ │
│ │ ▼ │
│ │ ┌───────┐ ┌───────┐ │
│ │ │ PCB 1 │ │ PCB 2 │ │
│ │ │ PID:7 │───▶ │ PID:9 │───▶ NULL │
│ │ │ State │ │ State │ │
│ │ └───────┘ └───────┘ │
│ └──────────────────────▲ │
│ │
│ 조건: 새 프로세스는 정책(RR, Priority)에 따라 큐의 끝 혹은 중간 삽입 │
└───────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이 그림의 핵심은 준비 큐가 묵직한 프로세스 자체를 이동시키는 것이 아니라, 오직 가벼운 PCB (Process Control Block) 내의 링크드 리스트 포인터만을 조작한다는 점이다. 새로운 프로세스가 준비 큐에 들어오면 커널은 Tail Pointer를 통해 O(1) 시간에 큐의 끝에 PCB를 매달 수 있다. 반대로 CPU가 비게 되면 Head Pointer가 가리키는 첫 번째 PCB가 선택되어 실행 상태로 전이되며 리스트에서 제거된다. 이 단순한 구조 덕분에 초당 수천 번 발생하는 상태 전이 상황에서도 커널이 메모리 복사 오버헤드 없이 신속하게 시스템 상태를 갱신할 수 있다.
O(1) 스케줄러와 다중 큐 동작 원리
단일 큐의 경우, 우선순위가 높은 프로세스를 찾기 위해 큐 전체를 순회해야 하는 O(N)의 비용이 발생한다. 현대 OS는 이를 해결하기 위해 O(1) 배열 구조를 채택했다.
- 배열 생성: 0~139(리눅스 기준)의 우선순위별로 별도의 큐(연결 리스트)를 생성한다.
- 비트맵 검사: 어떤 우선순위 큐에 프로세스가 존재하는지를 비트맵으로 캐싱하여 0이 아닌 첫 번째 비트를 찾는 하드웨어 명령어를 사용한다.
- 디스패치: 찾아낸 최고 우선순위 큐의 Head에서 PCB를 즉시 꺼내어 O(1)로 디스패치한다.
- 타임 슬라이스 소진: 실행을 마친 프로세스는 Expired 배열의 해당 우선순위 큐에 삽입된다.
- 포인터 스왑: Active 배열이 모두 비워지면, Active와 Expired 배열의 포인터를 스왑(Swap)하여 기아 현상(Starvation)을 방지한다.
- 📢 섹션 요약 비유: 수백 명의 대기열을 한 줄로 세우는 대신, 1번부터 140번까지 등급별 게이트를 만들어 비어있지 않은 가장 앞 번호 게이트의 사람을 즉시 입장시키는 체계와 같습니다.
Ⅲ. 융합 비교 및 다각도 분석
심층 기술 비교: 준비 큐 (Ready Queue) vs 대기 큐 (Wait Queue)
| 비교 항목 | 준비 큐 (Ready Queue) | 대기 큐 (Wait Queue / Device Queue) | 판단 포인트 |
|---|---|---|---|
| 대기 자원 | CPU (Central Processing Unit) | I/O 장치, 세마포어, 락 (Lock) | 시스템 병목 위치 파악 |
| 큐의 개수 | 시스템당 1개 혹은 코어당 1개 | 각 디바이스나 이벤트마다 개별 존재 | 모니터링 복잡도 |
| 스케줄링 알고리즘 | RR, MLFQ, CFS 기반 | 대부분 FIFO 또는 I/O 특화 스케줄링 | 알고리즘 적용 지점 |
| 상태 전이 방향 | Ready → Running | Wait → Ready | 상태 전이 시 트리거 |
| 성능 오버헤드 | 과부하 시 CPU 레이턴시 증가 | 락 경합 및 I/O 병목으로 TPS 저하 | 병목 튜닝 전략 |
이 두 큐 간의 프로세스 이동은 운영체제 성능 모니터링의 가장 핵심적인 지표가 된다. 멀티 코어 환경에서 각 CPU 코어가 독립적인 런큐(Runqueue)를 가지는 아키텍처를 살펴보면 동기화 이슈를 이해할 수 있다.
┌────────────────────────────────────────────────────────────────────────┐
│ 멀티 코어 시스템의 개별 런큐 (Per-Core Runqueue) 아키텍처 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ [Global Load Balancer] (간헐적 개입, 부하 재분배) │
│ │ │ │
│ ┌───────▼───────┐ ┌───────▼───────┐ │
│ │ Core 0 런큐 │ │ Core 1 런큐 │ │
│ │ ─▶ PCB_A │ │ ─▶ PCB_C │ │
│ │ ─▶ PCB_B │ │ ─▶ NULL │ │
│ └───────┬───────┘ └───────┬───────┘ │
│ │ │ │
│ [CPU 0] [CPU 1] │
│ │
│ 비교: 단일 글로벌 큐 사용 시 락 경합(Lock Contention)이 발생하므로 │
│ 코어별 독립 큐를 두어 로컬에서 락 없이 스케줄링(SMP 성능 향상) │
└────────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 과거 단일 프로세서 환경에서는 글로벌 준비 큐 하나만 두어도 충분했지만, 현대 SMP (Symmetric Multiprocessing) 환경에서는 코어 수가 급증하면서 단일 큐 접근 시 스핀락 (Spinlock) 경합이 치명적 병목이 되었다. 따라서 각 코어마다 독립적인 런큐(준비 큐)를 배치하여, CPU 0은 자신의 런큐에서만 O(1)로 프로세스를 꺼내 실행한다. 이는 코어 간 캐시 친화도(Cache Affinity)를 높이는 강력한 장점이 있으나, 코어 0은 과부하이고 코어 1은 노는 불균형이 생길 수 있다. 이를 해결하기 위해 글로벌 로드 밸런서가 주기적으로 개입하여 큐 간의 워크로드를 훔쳐오는(Work Stealing) 기법이 결합되어야만 전체 시스템의 처리량을 극대화할 수 있다.
과목 융합 관점
-
컴퓨터 구조 (CA, Computer Architecture): 준비 큐에서 디스패치될 때 일어나는 문맥 교환 (Context Switch)은 TLB (Translation Lookaside Buffer) 플러시와 캐시 미스를 유발하므로, 캐시 일관성 정책에 큰 영향을 준다.
-
소프트웨어 공학 (SE, Software Engineering): 멀티스레딩 애플리케이션 설계 시 코어 수보다 훨씬 많은 스레드를 생성하면, 대부분이 준비 큐에 적체되어 문맥 교환 비용만 증가하는 스래싱 (Thrashing) 현상이 발생한다.
-
📢 섹션 요약 비유: 마트의 계산대에서 한 줄 서기(글로벌 큐)를 할지, 계산대마다 따로 줄을 설지(코어별 큐)의 문제이며, 눈치껏 빈 계산대로 옮겨가는 행동(워크 스틸링)이 전체 대기 시간을 줄입니다.
Ⅳ. 실무 적용 및 기술사적 판단
실무 시나리오 및 판단
- 시나리오 — 고부하 웹 서버의 CPU 기아 (Starvation): 수천 개의 연결을 처리하는 웹 서버에서, 특정 배치(Batch) 분석 스레드가 낮은 우선순위를 받아 준비 큐 후미에서 영원히 디스패치되지 못하는 현상.
- 판단: 스케줄러가 에이징 (Aging) 기법을 제대로 적용하지 않았거나 고정 우선순위를 잘못 할당한 결과다. 실무에서는 동적 우선순위가 적용되는 스케줄링 클래스(예: 리눅스 SCHED_NORMAL)를 확인하고, nice 값을 튜닝하여 점진적으로 우선순위가 상승하도록 조치해야 한다.
- 시나리오 — 실시간 (Real-time) 제어 시스템의 응답 지연: 공장 자동화 로봇 제어 프로세스가 일반 사용자 프로세스와 섞여 준비 큐에 머무는 시간이 길어져 밀리초 단위의 데드라인을 위반.
- 판단: 실시간 프로세스는 철저히 분리된 실시간 런큐(SCHED_FIFO, SCHED_RR)에 배치해야 하며, 일반 프로세스가 선점할 수 없도록 커널 레벨의 우선순위 설정(chrt, taskset)이 필수적이다.
- 시나리오 — 우선순위 역전 (Priority Inversion): 높은 우선순위 프로세스가 준비 큐에서 대기 중인데, 락(Lock)을 쥔 낮은 우선순위 프로세스가 중간 우선순위 프로세스에게 CPU를 뺏겨 실행되지 못하는 교착 상태.
- 판단: 우선순위 상속 (Priority Inheritance) 프로토콜을 적용하여, 락을 점유한 하위 프로세스의 우선순위를 일시적으로 상위 프로세스만큼 끌어올려 신속히 락을 해제하도록 설계해야 한다.
이러한 상황을 진단하기 위한 실무 운영에서의 트러블슈팅 의사결정 트리를 도식화한다.
┌───────────────────────────────────────────────────────────────────────┐
│ 준비 큐 지연 현상 진단 및 조치 플로우 │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ [CPU Load Average 상승 탐지 (ex. top, vmstat)] │
│ │ │
│ ▼ │
│ Run Queue 길이(r)가 코어 수보다 큰가? │
│ ├─ 예 ─────▶ [System CPU 바인딩 분석 (vmstat)] │
│ │ │ │
│ │ └─▶ [스레드 풀 축소, 스케일 아웃] │
│ │ │
│ └─ 아니오 │
│ │ │
│ ▼ │
│ 특정 PID의 스케줄링 대기 시간(delay)이 긴가? │
│ ├─ 예 ─────▶ [Context Switch 오버헤드 파악 (pidstat)] │
│ │ │ │
│ │ └─▶ [CPU Affinity(taskset) 설정] │
│ │ │
│ └─ 아니오 ──▶ [Wait Queue 병목 의심 (I/O Wait 확인)] │
└───────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이 의사결정 흐름은 실무 서버 환경에서 성능 저하를 분석할 때 '준비 큐'의 상태를 가장 먼저 확인하는 이유를 보여준다. vmstat의 'r' 열이 나타내는 값이 바로 런큐(준비 큐)에서 대기 중인 프로세스의 수다. 코어 수가 8개인데 r 값이 지속적으로 20을 넘는다면, CPU 자체의 처리 용량을 초과한 스래싱 상태다. 이 경우 단순히 프로세스 우선순위를 조정하는 것을 넘어, 애플리케이션의 스레드 풀 크기를 물리적 코어 수에 맞게 축소하여 준비 큐의 과적을 해소하거나 서버 증설(Scale-out)을 결정해야 한다. 반면 전체 큐는 짧은데 특정 프로세스만 지연된다면, 캐시 미스를 막기 위해 특정 코어에 프로세스를 고정(CPU Affinity)시키는 미시적 튜닝이 요구된다.
도입 체크리스트 및 안티패턴
-
운영적 체크리스트: 운영체제의 스케줄링 틱(Tick) 단위(HZ) 설정이 애플리케이션 요구사항과 부합하는가? 컨테이너(Docker 등) 환경에서 CPU 쿼터(Quota) 설정이 준비 큐의 스로틀링(Throttling)을 과도하게 유발하지 않는가?
-
안티패턴: 지나치게 세밀한 다단계 큐 분할. 큐 관리를 위한 스케줄러 자체의 CPU 점유율 오버헤드가 사용자 프로세스 실행 시간을 갉아먹는 배보다 배꼽이 더 큰 상황을 초래한다.
-
📢 섹션 요약 비유: 도로의 차선(코어)을 늘리지 않은 채, 신호등 체계(스케줄러)만 복잡하게 꼬아놓으면 오히려 차선 변경 오버헤드 때문에 전체 교통 체증이 더 악화되는 것과 같습니다.
Ⅴ. 기대효과 및 결론
정량/정성 기대효과
| 구분 | 비효율적 큐 관리 시 | 현대적 O(1) / CFS 적용 시 | 개선 효과 |
|---|---|---|---|
| 정량 | 스케줄링 탐색 시간 O(N) | 스케줄링 탐색 시간 O(1) 또는 O(log N) | CPU 오버헤드 90% 감소 |
| 정량 | 캐시 미스 지속 발생 | 코어별 로컬 런큐 + Affinity 보장 | 메모리 레이턴시 50% 이상 절감 |
| 정성 | I/O 바운드 프로세스 기아 | 대화형 프로세스 응답성 자동 보장 | 사용자 체감 반응 속도 극대화 |
미래 전망 및 표준
-
CFS (Completely Fair Scheduler)의 진화: 리눅스의 CFS는 전통적인 다단계 큐 대신 레드-블랙 트리(Red-Black Tree)를 사용하여 가상 런타임(vruntime)을 기반으로 준비 큐를 관리한다. 향후 BPF(eBPF)를 이용해 시스템 부하 특성에 맞춰 스케줄링 로직을 사용자 공간에서 동적으로 주입하는 형태가 표준이 될 것이다.
-
에너지 인지 스케줄링 (EAS): 모바일 및 클라우드 환경에서는 단순히 빠른 처리가 아니라, 준비 큐의 워크로드를 파악해 코어의 주파수(DVFS)를 조절하고 빅-리틀(big.LITTLE) 아키텍처에 맞춰 큐를 분배하는 전력 최적화 지향 구조로 발전하고 있다.
-
📢 섹션 요약 비유: 단순한 선착순 탑승에서 벗어나, 승객의 특성과 놀이기구의 에너지 소모량까지 계산하여 가장 스마트한 순서로 안내하는 AI 매니저 시스템으로 진화하는 과정입니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| 문맥 교환 (Context Switch) | 준비 큐에서 디스패치되어 실행 상태로 넘어갈 때 발생하는 레지스터 교체 작업으로, 빈도가 높을수록 시스템 부하를 가중시킨다. |
| CFS (Completely Fair Scheduler) | 리눅스의 기본 스케줄러로, 준비 큐를 레드-블랙 트리 형태로 구성하여 공정성을 수학적으로 보장한다. |
| 다단계 피드백 큐 (MLFQ) | 프로세스의 특성(I/O vs CPU 바운드)에 따라 여러 준비 큐 사이를 동적으로 승급/강등시키는 스케줄링 프레임워크다. |
| 디스패처 (Dispatcher) | 스케줄러가 준비 큐에서 고른 프로세스에게 실질적으로 CPU 제어권을 넘겨주는 커널의 핵심 컴포넌트다. |
| 우선순위 역전 (Priority Inversion) | 대기 큐의 락 문제와 결합되어, 준비 큐 내의 높은 우선순위 프로세스가 낮은 프로세스 때문에 실행을 못 하는 이상 현상이다. |
👶 어린이를 위한 3줄 비유 설명
- 준비 큐는 놀이터에서 롤러코스터(CPU)를 타기 위해 표를 끊고 기다리는 친구들이 서 있는 대기줄이에요.
- 스케줄러라는 안내 요원이 대기줄을 관리하는데, 얌전히 기다리는 친구나 화장실(I/O)에 다녀와서 급한 친구를 적절히 앞으로 보내줘서 싸움이 안 나게 해줘요.
- 이 줄을 똑똑하게 관리하지 않으면 힘 센 친구만 계속 놀이기구를 타고, 다른 친구들은 영원히 롤러코스터를 못 타는 슬픈 일이 벌어진답니다!