준비된 작업 중 누구에게 CPU를 줄지 정하는 것.
선점과 비선점
- 비선점 — 스스로 양보할 때까지 기다린다
- 하나가 무한 루프면 시스템이 멈춘다
- 선점 — 타이머 인터럽트로 강제로 빼앗는다
- 현대 범용 OS 는 전부 선점형
라운드로빈 — 공평한 출발점
각자 타임 슬라이스(quantum)만큼 쓰고 뒤로 간다
- 슬라이스가 짧으면 — 응답이 빠르다 · 전환 비용이 커진다
- 슬라이스가 길면 — 전환이 적다 · 대화형 작업이 느리게 느껴진다
대개 수 ms ~ 수십 ms 사이에서 절충한다
우선순위와 그 함정
중요한 걸 먼저 → 낮은 우선순위가 영영 못 도는 기아(starvation)
- 오래 기다린 작업의 우선순위를 올려 준다 (aging)
우선순위 역전 낮은 우선순위 L 이 락을 쥐고 있다 높은 우선순위 H 가 그 락을 기다린다 중간 우선순위 M 이 CPU 를 계속 차지한다
- L 이 못 돌아 락을 못 풀고, H 는 M 보다 높은데도 못 돈다
화성 탐사선 Pathfinder 가 실제로 이 문제로 재부팅을 반복했다
처방: 우선순위 상속 — 락을 쥔 L 을 H 의 우선순위로 잠시 올린다
MLFQ — 행동을 보고 정한다
-
여러 단계의 큐를 두고, 시작은 가장 높은 큐에서
-
타임 슬라이스를 다 쓰면 → 아래 큐로 내린다 (CPU 바운드로 판단)
-
슬라이스를 다 안 쓰고 I/O 로 양보하면 → 그 자리 유지 (대화형으로 판단)
-
주기적으로 전부 위로 올린다 (기아 방지)
-
"미리 알 수 없으니 관찰해서 추정한다" 는 발상
리눅스 CFS — 비율로 공평하게
각 작업의 '가상 실행 시간(vruntime)' 을 추적한다
- 가장 적게 쓴 작업에게 CPU 를 준다
- nice 값이 낮을수록 vruntime 이 천천히 늘어 더 많이 돈다
타임 슬라이스를 고정하지 않고 '목표 지연' 안에서 나눈다
- 작업이 많아지면 각자의 몫이 자동으로 줄어든다
nice -n 10 ./batch_job # 낮은 우선순위로 실행
chrt -f 50 ./realtime_task # 실시간 스케줄링 정책
cat /proc/<pid>/sched | head
면접 함정
- ❌ "우선순위를 높이면 빨라진다" → CPU 경합이 없으면 아무 차이가 없다. I/O 병목에도 무의미하다.
- ❌ "CFS는 완전히 공평하다" → nice 값에 따른 가중치 비례 공평이다.
I/O 바운드와 CPU 바운드
-
CPU 바운드 — 슬라이스를 끝까지 쓴다 → 우선순위가 내려간다 (MLFQ)
-
I/O 바운드 — 금방 블로킹된다 → 우선순위가 유지된다
-
왜 I/O 바운드를 우대하나
- 어차피 곧 블로킹되므로 CPU 를 오래 안 쓴다
- 먼저 보내 주면 I/O 를 빨리 시작해 장치를 놀리지 않는다
- 대개 대화형이라 응답성이 중요하다
실시간 스케줄링
- SCHED_FIFO — 우선순위가 높으면 스스로 놓을 때까지 계속 돈다
- SCHED_RR — 같은 우선순위끼리는 라운드로빈
- SCHED_OTHER — 일반 (CFS)
실시간 정책은 '데드라인이 있는' 작업용이다 잘못 쓰면 시스템 전체가 멈춘다 (무한 루프가 CPU 를 놓지 않는다)
chrt -p <pid> # 현재 정책·우선순위
chrt -f 50 ./task # FIFO 우선순위 50
sysctl kernel.sched_rt_runtime_us # 실시간 작업의 CPU 상한 (안전장치)
로드 애버리지의 의미
uptime → load average: 4.2 3.8 2.1 (1분 · 5분 · 15분)
리눅스에서는 '실행 중 + 실행 대기 + D 상태(I/O 대기)' 의 평균이다
→ CPU 사용률과 다르다. 디스크가 느려도 로드가 치솟는다
→ 코어 수와 비교해야 의미가 있다 (8코어에서 4.2 는 여유)