05. Sched

  • Haram Lee
  • 2026-04-14
  • studies / 26-1 / operating-systems

CPU Scheduling

  • 어떤 기준으로 좋은 스케줄러를 평가하는지
  • 어떤 가정 아래에서 어떤 알고리즘이 좋은지
  • 인터랙티브 작업과 CPU-bound 작업을 같이 다룰 때 왜 문제가 생기는지
  • 현대 OS가 왜 MLFQ나 proportional share 같은 방식을 쓰는지
    • turnaround를 줄이고 싶으면 SJF/STCF 쪽, response time을 좋게 하고 싶으면 RR 쪽, 현실 시스템에선 interactive와 CPU-bound를 같이 처리해야 하므로 MLFQ 쪽, 비율대로 공정하게 나누고 싶으면 proportional share/lottery/stride 쪽으로…
  • mechanism: 어떻게 전환할 것인가
  • policy: 언제, 누구로 전환할 것인가
  • workload
    • 작업의 집합. 각 작업의 도착 시간(arrival time), 실행 시간(run time) 같은 정보가 workload를 이룬다.
  • scheduler
    • 언제 어떤 job을 돌릴지 결정하는 로직
  • metric
    • 스케줄링 품질을 재는 기준

Scheduling Performance Metrics

  1. Turnaround time
    • 완료까지 걸린 시간: T_{turnaround} = T_{completion} - T_{arrival}
    • 예를 들어 어떤 작업이 0 초에 도착해서 30 초에 끝났다면 turnaround time은 30 이다.
    • 배치 작업처럼 “전체 완료가 빨라야” 하는 경우 중요하다.
  2. Response time
    • 처음 CPU를 받기까지 걸린 시간: T_{response} = T_{firstrun} - T_{arrival}
    • 예를 들어 사용자가 터미널에 명령을 쳤는데 3 초 뒤에야 첫 출력이 뜨면 response time은 3 초다.
    • 인터랙티브 시스템에서는 이 값이 매우 중요하다. “전체 완료는 좀 늦어도 일단 반응은 빨라야” 사용자 입장에서 좋기 때문이다.
  3. Waiting time
    • Ready queue에서 기다린 시간이다.
    • CPU는 아직 못 받고 대기열에 서 있던 시간이라고 생각하면 된다.
  4. Throughput
    • 단위 시간당 얼마나 많은 작업을 완료했는가를 나타낸다.
    • 예를 들어 1초에 100개의 job을 끝내면 throughput이 높은 것이다.
  5. Resource utilization
    • CPU, 디스크 같은 비싼 자원을 얼마나 놀리지 않고 잘 쓰는가를 본다.
  6. Overhead / Fairness
    • context switch가 너무 많으면 overhead가 커진다.
    • 특정 프로세스만 계속 돌면 fairness가 나빠진다.
    • 따라서 scheduler는 오버헤드는 줄이고 fairness는 높이는 방향으로 설계되어야 한다.

Workload Assumptions

  • 처음에는 분석을 쉽게 하려고 다음과 같은 단순한 가정을 둔다.
  1. 각 job의 실행 시간은 같다.
  2. 모든 job은 동시에 도착한다.
  3. 한 번 시작하면 끝날 때까지 계속 실행된다.
  4. 모든 job은 CPU만 사용하고 I/O는 없다.
  5. 각 job의 실행 시간을 미리 안다.
  • 이 단계에서는 주로 turnaround time을 중심으로 본다.

FIFO

  • FIFO = First-Come, First-Served
  • 먼저 도착한 job을 먼저 실행한다.
  • non-preemptive 방식이다.
  • starvation은 없지만, convoy effect 문제가 있다.
  • 예시 1
    • A(10), B(10), C(10) 이 동시에 도착.
    • 실행 순서: A \rightarrow B \rightarrow C
    • 완료 시간: A = 10 , B = 20 , C = 30
    • 평균 turnaround time: \frac{10 + 20 + 30}{3} = 20
  • 예시 2: convoy effect
    • A(100), B(10), C(10) 이 동시에 도착하고 A 가 먼저 실행됨.
    • 실행 순서: A \rightarrow B \rightarrow C
    • 완료 시간: A = 100 , B = 110 , C = 120
    • 평균 turnaround time: \frac{100 + 110 + 120}{3} = 110
    • 짧은 작업 B, C 가 긴 작업 A 뒤에서 오래 기다리게 된다.
    • 이게 convoy effect다.

SJF

  • SJF = Shortest Job First
  • 실행 시간이 가장 짧은 job부터 먼저 실행한다.
  • non-preemptive 방식이다.
  • 주어진 가정 아래에서는 평균 turnaround time이 최적이다.
  • 예시
    • A(100), B(10), C(10)
    • 실행 순서: B \rightarrow C \rightarrow A
    • 완료 시간: B = 10 , C = 20 , A = 120
    • 평균 turnaround time: \frac{10 + 20 + 120}{3} = 50
    • FIFO의 110 보다 훨씬 좋아진다.
    • 짧은 작업을 먼저 끝내면 평균 완료 시간이 크게 줄어든다.
  • 한계
    • job의 실행 시간을 현실에서는 미리 알기 어렵다.
    • job이 동시에 도착하지 않으면 항상 최적이 아니다.
    • 긴 작업이 계속 밀릴 수 있어서 starvation 가능성이 있다.

Preemptive Scheduling

  • non-preemptive scheduling에서는 현재 job이
    • I/O를 하거나
    • yield() 하거나
    • exit 해야만 다른 job이 CPU를 받을 수 있다.
  • preemptive scheduling에서는 scheduler가 현재 프로세스를 강제로 중단하고 context switch를 일으킬 수 있다.

STCF

  • STCF = Shortest Time-to-Completion First
  • SJF의 preemptive 버전이다.
  • 핵심은 남은 실행 시간이 가장 짧은 job을 계속 선택하는 것이다.
  • 규칙
    • 현재 실행 중인 job보다 더 짧은 새 job이 도착하면, 현재 job을 preempt하고 새 job을 먼저 실행한다.
  • 예시
    • A(100) 이 먼저 시작
    • 나중에 B(10), C(10) 도착
    • SJF에서는 이미 A 가 시작했으므로 끝까지 가서 평균 turnaround가 나빠질 수 있다.
    • STCF에서는 B, C 가 들어오자마자 A 를 중단하고 먼저 처리한다.
    • 평균 turnaround time: \frac{120 + 10 + 20}{3} = 50
    • 짧은 작업을 빨리 끝내서 평균 완료 시간을 크게 줄일 수 있다.

RR

  • RR = Round Robin
  • run queue를 원형 FIFO처럼 보고, 각 job에 time slice(quantum) 를 조금씩 나눠 준다.
  • preemptive 방식이다.
  • starvation은 없다.
  • response time을 개선하는 데 강하다.
  • quantum
    • 너무 짧으면 context switch overhead가 커진다.
    • 너무 길면 responsiveness가 나빠진다.
    • 보통 10 \sim 100ms 정도를 쓴다.
  • 예시
    • A(30), B(30), C(30)
    • quantum = 10
    • SJF일 때
      • 실행 순서: A \rightarrow B \rightarrow C
      • 평균 turnaround time: \frac{30 + 60 + 90}{3} = 60
      • 평균 response time: \frac{0 + 30 + 60}{3} = 30
    • RR일 때
      • 실행 순서: A \rightarrow B \rightarrow C \rightarrow A \rightarrow B \rightarrow C \rightarrow A \rightarrow B \rightarrow C
      • 평균 turnaround time: \frac{70 + 80 + 90}{3} = 80
      • 평균 response time: \frac{0 + 10 + 20}{3} = 10
  • RR은 turnaround는 나빠질 수 있지만, response time은 훨씬 좋아진다.
  • 그래서 time-sharing 환경에 적합하다.

(Static) Priority Scheduling

  • 각 job에 고정 priority를 준다.
  • priority가 높은 job을 먼저 실행한다.
  • 같은 priority끼리는 RR이나 FIFO를 쓸 수 있다.
  • preemptive일 수도 있고 non-preemptive일 수도 있다.
  • 한계
    • 높은 priority job이 계속 들어오면 낮은 priority job은 영원히 실행되지 못할 수 있다.
    • 즉 starvation 문제가 생긴다.

Incorporating I/O

  • 현실의 workload는 CPU만 쓰지 않는다.
  • 어떤 job은 짧게 CPU를 쓰고 곧바로 I/O를 기다리고,
  • 어떤 job은 CPU를 길게 점유한다.
  • 따라서 scheduler는 I/O를 고려해야 한다. 슬라이드는 이를 I/O-aware scheduling이라고 설명한다.
  • 핵심 아이디어
    • computation과 I/O를 겹쳐서 자원 활용도를 높인다.
    • 각 CPU burst를 하나의 독립적인 job처럼 취급한다.
  • 예시
    • A : interactive job
    • B : CPU-intensive job
    • Aread()를 호출하고 디스크를 기다리는 동안 CPU를 놀리지 않고 B 를 실행시키면 전체 자원 활용도가 올라간다.
    • 즉 좋은 scheduler는 CPU와 disk를 겹쳐서 사용하려고 한다.

xv6 CPU Scheduler

  • xv6는 기본적으로 Round Robin scheduling을 사용한다.
  • 매 timer IRQ마다 process에게 yield를 강제한다.
  • yield()
    • 현재 프로세스를 RUNNABLE 상태로 바꾸고 sched()를 호출한다.
    • 즉 “나는 CPU를 내놓고 ready queue로 돌아간다"는 뜻이다.
  • scheduler()
    • CPU별로 무한 루프를 돌면서 process table을 순회한다.
    • RUNNABLE인 프로세스를 찾으면 그 프로세스를 RUNNING 상태로 바꾸고 실행시킨다.
    • 다시 돌아오면 또 다음 runnable process를 찾는다.
  • trap에서 timer IRQ 처리
    • timer interrupt가 오면 현재 process가 RUNNING 상태일 때 yield()를 호출한다.
    • 이걸 통해 RR이 구현된다.

Towards a General CPU Scheduler

  • 목표
    • turnaround time 최적화
    • interactive job의 response time 최소화
  • 문제
    • 현실에서는 workload를 미리 알 수 없다.
    • job의 실행 시간을 사전에 정확히 아는 경우가 드물다.
  • 따라서 scheduler는 과거 행동을 보고 미래를 예측해야 한다.

MLFQ

  • MLFQ = Multi-Level Feedback Queue
  • priority level마다 여러 queue를 두고,
    • queue 사이에서는 priority scheduling
    • 같은 queue 안에서는 RR을 사용한다.
  • 핵심은 priority가 고정이 아니라, 관찰된 행동에 따라 변한다는 점이다.
  • 기본 규칙
    • Rule 1: Priority(A) > Priority(B) 이면 A 가 실행된다.
    • Rule 2: Priority(A) = Priority(B) 이면 A, B 는 RR로 실행된다.

Changing Priority

  • 전형적인 workload는 보통 두 종류가 섞여 있다.
  • interactive jobs
    • 짧게 실행되고 빠른 response time이 중요하다.
  • CPU-intensive jobs
    • CPU를 오래 사용하고 response time은 덜 중요하다.
  • Attempt #1 규칙
    • Rule 3: 새 job은 가장 높은 priority queue에 들어간다.
    • Rule 4a: time slice를 전부 다 쓰면 priority를 한 단계 낮춘다.
    • Rule 4b: time slice를 다 쓰기 전에 CPU를 내놓으면 같은 priority를 유지한다.
  • 직관
    • 처음엔 모든 job을 interactive하다고 가정하고 좋은 대우를 해 준다.
    • CPU를 오래 계속 쓰는 job은 “CPU-bound겠구나” 하고 점점 아래 queue로 내린다.
    • 짧게 쓰고 자주 I/O 하러 가는 job은 높은 priority를 유지한다.

Scheduling Under Rules 1-4

  • long-running job은 점점 아래 queue로 밀린다.
  • short-running, interactive job은 위 queue에 남아서 빠르게 반응한다.
  • 그래서 MLFQ는 인터랙티브 workload에 유리하다.

Priority Boost

  • Attempt #1에는 문제가 있다.
    • long-running job이 starvation 될 수 있다.
    • malicious user가 time slice 끝나기 직전에 CPU를 내놓으며 scheduler를 속일 수 있다.
    • 프로그램의 성격이 시간에 따라 변할 수도 있다.
  • 해결 규칙
    • Rule 5: 일정 시간 S 가 지나면 시스템의 모든 job을 topmost queue로 올린다.
  • 효과
    • 아래 queue에 오래 있던 job도 다시 CPU를 받을 기회를 얻는다.
    • starvation을 완화한다.

Better Accounting

  • Rule 4a/4b만 쓰면 process가 scheduler를 속일 수 있다.
  • 그래서 슬라이드는 Rule 4를 더 정밀하게 바꾼다.
  • 새 Rule 4
    • 어떤 level에서 할당된 총 CPU allotment를 다 쓰면,
    • 몇 번에 나눠 썼는지와 관계없이
    • priority를 낮춘다.
  • 의미
    • “한 번에 quantum을 다 썼는가?“가 아니라,
    • “그 level에서 누적해서 얼마나 CPU를 썼는가?“를 본다.
    • 그래서 잘게 나눠 쓰면서 priority를 유지하려는 꼼수를 막을 수 있다.

MLFQ Final Rules

  1. priority가 높은 job이 먼저 실행된다.
  2. 같은 priority면 RR을 사용한다.
  3. 새 job은 가장 높은 priority에서 시작한다.
  4. 한 level에서 allotted CPU time을 다 쓰면 아래 queue로 내려간다.
  5. 일정 시간마다 모든 job을 top queue로 boost한다.
  • Beauty of MLFQ
    • 각 process의 CPU usage를 미리 알 필요가 없다.
    • scheduler가 실행 패턴을 관찰하면서 추정해 나간다.

UNIX Scheduler

  • 실제 UNIX 계열 scheduler는 대체로 preemptive priority scheduling + time slice 기반이다.
  • process priority는 동적으로 변한다.
  • interactive process를 CPU-bound process보다 더 우대한다.
  • aging을 통해 starvation을 줄인다.
    • 오래 기다리면 priority를 올리고
    • CPU를 많이 쓰면 priority를 내린다.

Proportional Share Scheduling

  • 각 process에 weight를 주고 CPU를 그 비율대로 나눈다.
  • 관점은 “누가 먼저 실행될까?“보다 “누가 CPU를 몇 퍼센트 가져야 할까?“에 가깝다.
  • 예시
    • A: 25\%
    • B: 12.5\%
    • C: 50\%
    • D: 12.5\%
    • 장기적으로 CPU 사용량도 이 비율에 가까워져야 한다.

Lottery Scheduling

  • proportional share를 구현하는 한 가지 방법이다.
  • 각 process는 ticket을 가진다.
  • ticket 수가 많을수록 CPU를 받을 확률이 높다.
  • random number generator로 당첨 ticket을 뽑아 그 process를 실행한다.
  • 예시
    • A: 50 tickets
    • B: 30 tickets
    • C: 20 tickets
    • 장기적으로 CPU 비율은 대략 A: 50\% , B: 30\% , C: 20\% 에 가까워진다.

Stride Scheduling

  • lottery scheduling의 deterministic 버전이다.
  • stride는 ticket 수에 반비례한다.
  • pass value가 가장 작은 process를 선택하고, 그 process의 pass에 자기 stride를 더한다.
  • 아이디어: stride \propto \frac{1}{tickets}
    • ticket이 많을수록 stride가 작다.
    • stride가 작을수록 pass가 천천히 증가하므로 더 자주 선택된다.
  • 예시
    • \tau_1 : tickets = 3 , stride = 2
    • \tau_2 : tickets = 2 , stride = 3
    • \tau_3 : tickets = 1 , stride = 6
    • 매번 pass가 가장 작은 task를 골라 실행한다.
    • 실행한 task의 pass에 stride를 더한다.
    • 그러면 장기적으로 실행 비율이 ticket 비율에 맞춰진다.

Summary

  • 배치 작업 중심
    • FIFO: 단순하지만 convoy effect
    • SJF: 평균 turnaround 최적
    • STCF: SJF의 preemptive 버전
  • 인터랙티브 작업 중심
    • RR: response time 개선
    • static priority: 중요도 반영 가능하지만 starvation 가능
  • 현실적 scheduler
    • I/O-aware scheduling 필요
    • xv6는 RR 기반
    • 일반적인 현대 시스템은 workload를 모르므로 관찰 기반 scheduler가 필요
    • 대표적으로 MLFQ가 있다.
  • 공정한 비율 분배
    • proportional share
    • lottery scheduling
    • stride scheduling
  • 한 줄 요약
    • turnaround를 줄이고 싶으면 SJF/STCF 쪽
    • response time을 좋게 하고 싶으면 RR 쪽
    • interactive와 CPU-bound를 함께 다루려면 MLFQ 쪽
    • CPU를 비율대로 공정하게 나누고 싶으면 proportional share / lottery / stride 쪽으로 간다.
Discussion