Dispatcher와 Context Switching: 스케줄러의 선택을 CPU 실행으로 옮기는 과정
Dispatcher의 Context Switching, Kernel·User 모드 전환, Dispatch Latency와 Scheduler 역할 분담을 운영체제 관점에서 정리한다.
2026-08-14 · 최초 발행 2026-01-03
CPU 제어권을 넘기는 Dispatcher
Scheduler가 다음에 실행할 프로세스를 고르면, Dispatcher가 그 결정을 CPU 실행 상태로 옮긴다. 이 모듈은 현재 프로세스의 레지스터와 프로그램 카운터(PC)를 PCB에 저장하고, 다음 프로세스의 PCB에서 상태를 복원한 뒤 Kernel 모드에서 User 모드로 전환한다.
따라서 Scheduler와 Dispatcher는 같은 일을 반복하지 않는다. Scheduler는 FIFO, Round Robin, Priority 같은 알고리즘으로 실행 대상을 결정하는 정책 계층이고, Dispatcher는 선택된 대상을 실제로 전환하는 메커니즘 계층이다.
Scheduler: "다음은 Process B를 실행하자"
Dispatcher: "알겠습니다. Process B로 전환합니다"
전환 시 보존하고 되살리는 실행 상태
Context Switching에서 Dispatcher는 현재 프로세스의 CPU 상태를 PCB에 기록한다. 여기에는 CPU 레지스터 값, 프로그램 카운터(PC), 스택 포인터(SP), 플래그 레지스터가 포함된다.
다음 프로세스로 넘어갈 때는 해당 PCB에서 같은 상태를 복원한다. PC가 복원되면 프로세스는 이전에 멈췄던 다음 명령어 주소부터 실행을 재개한다.
또한 Dispatcher는 Kernel 모드에서 동작하고, 전환이 끝난 프로세스는 User 모드에서 실행된다. 권한 레벨은 x86에서 Ring 0(Kernel)에서 Ring 3(User)로, ARM에서는 EL1(Kernel)에서 EL0(User)로 바뀐다.
// Process A 실행 중
PC = 0x1234 // 현재 명령어 주소
// Context Switch 발생
// Dispatcher가 Process A의 PC를 PCB_A에 저장
PCB_A.PC = 0x1234
// Process B로 전환
// Dispatcher가 Process B의 PC를 복원
PC = PCB_B.PC // 예: 0x5678
// Process B가 0x5678부터 실행 재개
인터럽트부터 다음 프로세스 재개까지
프로세스 전환은 인터럽트나 System Call로 시작한다. CPU가 Kernel 모드로 들어오면 Dispatcher가 현재 상태를 저장하고, Scheduler가 다음 실행 대상을 선택한다. 이어 Dispatcher가 선택된 프로세스의 상태를 복원하고 User 모드로 돌아가 실행을 재개한다.
// Pseudo code
void save_context(PCB *current) {
current->registers.eax = CPU.eax;
current->registers.ebx = CPU.ebx;
current->registers.ecx = CPU.ecx;
// ... 모든 레지스터 저장
current->registers.eip = CPU.eip; // PC
current->registers.esp = CPU.esp; // SP
current->page_table = CPU.CR3; // 페이지 테이블
}
PCB *next = scheduler_select_next(); // 다음 프로세스 선택
void restore_context(PCB *next) {
CPU.eax = next->registers.eax;
CPU.ebx = next->registers.ebx;
CPU.ecx = next->registers.ecx;
// ... 모든 레지스터 복원
CPU.eip = next->registers.eip; // PC 복원
CPU.esp = next->registers.esp; // SP 복원
CPU.CR3 = next->page_table; // 페이지 테이블 전환
}
; x86 assembly
iret ; Interrupt Return (Kernel → User)
Dispatch Latency가 가리키는 전환 비용
Dispatch Latency는 프로세스 전환에 걸리는 시간이다. 현재 프로세스를 멈추는 Stop Time과 다음 프로세스를 시작하는 Start Time의 합으로 본다.
Stop Time은 현재 프로세스의 인터럽트 처리, 레지스터 저장, 자원 정리를 포함하며 일반적으로 12 마이크로초다. Start Time에는 Scheduler의 다음 프로세스 선택, 레지스터 복원, 페이지 테이블 전환이 들어가며 일반적으로 35 마이크로초가 걸린다.
총 Dispatch Latency는 일반 OS에서 520 마이크로초, 실시간 OS에서 110 마이크로초, 임베디드 RTOS에서 수백 나노초다.
전환 시간을 줄이는 방법은 하드웨어와 소프트웨어 양쪽에 있다. 레지스터 세트 다중화(Hyper-Threading), ASID/PCID를 통한 TLB 플러시 최소화, FPU 레지스터를 늦게 저장하는 Lazy Context Switching, 단순한 경우를 우선 처리하는 Fast Path가 해당한다. PCB와 Dispatcher 코드를 캐시에 유지하는 방식도 캐시 미스를 줄이는 데 사용된다.
정책을 고르는 Scheduler와 전환을 수행하는 Dispatcher
| 항목 | Scheduler | Dispatcher |
|---|---|---|
| 역할 | 다음 프로세스 선택 | 선택된 프로세스로 전환 |
| 목적 | 정책 (Policy) 결정 | 메커니즘 (Mechanism) 구현 |
| 알고리즘 | FIFO, Round Robin, Priority 등 | Context Switching |
| 실행 시간 | 가변 (알고리즘에 따라) | 짧고 고정적 |
| 측정 지표 | 스케줄링 오버헤드 | Dispatch Latency |
실행 흐름에서는 인터럽트가 발생한 뒤 Dispatcher가 현재 상태를 저장한다. Scheduler가 다음 프로세스를 고르고, Dispatcher가 그 상태를 복원한 뒤 User 모드로 전환한다.
Scheduler가 의사처럼 진단과 처방을 결정한다면, Dispatcher는 그 처방을 실행하는 간호사에 가깝다.
운영체제에서의 Dispatcher 구현
Linux의 schedule()은 현재 태스크를 확인하고 pick_next_task()로 다음 태스크를 고른다. 이전 태스크와 다음 태스크가 다르면 context_switch()가 메모리 관리 정보와 CPU 상태를 전환한다.
// kernel/sched/core.c
asmlinkage __visible void __sched schedule(void)
{
struct task_struct *prev, *next;
prev = current; // 현재 프로세스
// Scheduler: 다음 프로세스 선택
next = pick_next_task(rq, prev);
if (prev != next) {
// Dispatcher: Context Switch
context_switch(rq, prev, next);
}
}
static inline void context_switch(struct rq *rq,
struct task_struct *prev,
struct task_struct *next)
{
// 메모리 관리 정보 전환
switch_mm(prev->mm, next->mm);
// CPU 상태 전환 (레지스터)
switch_to(prev, next);
}
Windows에서는 KiDispatchThread가 Dispatcher의 메인 함수이며, KiSwitchThread가 Context Switching을 수행한다. KeFindReadyThread가 다음 프로세스를 선택하고, KiSwitchThread와 SwapContext가 레지스터 저장·복원을 담당한다.
FreeRTOS에서는 현재 Task를 저장한 뒤 우선순위가 가장 높은 다음 Task를 선택하고, 해당 Context를 복원한다. 예측 가능한 Dispatch Latency, 엄격한 우선순위 준수, 최소한의 오버헤드가 특징이다.
void vTaskSwitchContext(void)
{
// 현재 Task 저장
vPortYield();
// 다음 Task 선택 (Scheduler)
taskSELECT_HIGHEST_PRIORITY_TASK();
// Context Switch (Dispatcher)
portRESTORE_CONTEXT();
}
전환 경로와 캐시를 다루는 방법
동일 프로세스로 돌아가는 경우에는 Context Switch 자체가 필요하지 않다. Fast Dispatch는 이런 자주 쓰이는 경로를 빠르게 처리하고, 다른 프로세스로 전환해야 할 때만 느린 경로를 탄다.
// Fast path: 동일 프로세스로 복귀
if (prev == next) {
return; // Context Switch 불필요
}
// Slow path: 다른 프로세스로 전환
context_switch(prev, next);
Intel Hyper-Threading은 물리 코어 하나에 논리 코어 두 개와 레지스터 세트 두 개를 둔다. 이 경우 Context Switch 시 레지스터 저장·복원이 필요하지 않다. ASID/PCID는 TLB 항목에 프로세스 ID 태그를 두어 Context Switch 때 TLB 플러시를 피하고 TLB 미스를 줄인다.
PCB를 캐시에 유지하면 메모리 접근 시간이 줄고, Dispatcher 코드를 Instruction Cache에 고정하면 캐시 미스를 막을 수 있다. CPU Affinity로 프로세스를 특정 CPU에 고정하는 방식은 캐시 지역성을 높여 Dispatcher 오버헤드를 줄인다.