4.1 Bài toán scheduling
Máy bạn có 4 CPU core nhưng đang chạy hàng trăm process (Chrome, VS Code, system services...). Tại mọi thời điểm, mỗi core chỉ chạy 1 thread. Vậy ai chạy, ai chờ?
Scheduler là phần của kernel quyết định: tại mỗi thời điểm, thread/process nào sẽ được CPU. Nó chạy hàng nghìn lần mỗi giây, mỗi lần đưa ra quyết định trong micro giây.
Phân loại theo loại workload
- CPU-bound (compute): dành phần lớn thời gian tính toán, ít I/O. Vd: render video, train ML.
- I/O-bound: dành nhiều thời gian đợi I/O (disk, network, user input). Vd: database, web server, text editor.
Scheduler hiện đại phải cân bằng: ưu tiên I/O-bound (để app responsive) nhưng không bỏ đói CPU-bound.
4.2 Tiêu chí đánh giá scheduler
| Metric | Định nghĩa | Mục tiêu |
|---|---|---|
| CPU Utilization | % thời gian CPU bận | Càng cao càng tốt (tới ~85%) |
| Throughput | Số process xong mỗi đơn vị thời gian | Càng cao càng tốt |
| Turnaround time | Tổng thời gian từ submit đến xong = wait + run | Càng thấp càng tốt |
| Waiting time | Thời gian process đợi trong ready queue | Càng thấp càng tốt |
| Response time | Thời gian từ submit đến lần đầu được CPU | Càng thấp càng tốt (cho UI) |
| Fairness | Mọi process được phục vụ công bằng | Quan trọng — tránh starvation |
Các metric này thường mâu thuẫn nhau: tối ưu throughput có thể hi sinh response time. Chọn scheduler là chọn trade-off.
4.3 Preemptive vs Non-preemptive
Non-preemptive
- Process giữ CPU đến khi tự nguyện nhường (block I/O hoặc terminate)
- Đơn giản, không cần đồng bộ phức tạp
- Vấn đề: 1 process nặng có thể "hog" CPU, làm cả hệ thống lag
- Vd: cooperative multitasking (Mac OS Classic)
Preemptive
- OS có thể "cướp" CPU bất cứ lúc nào (timer interrupt)
- Tốt cho responsiveness, fairness
- Cần thiết cho mọi OS hiện đại
- Vd: Linux, macOS, Windows
Cơ chế preemption: hardware timer phát interrupt mỗi vài ms. Kernel handle interrupt → chạy scheduler → có thể đổi process.
4.4 FCFS — First-Come, First-Served
Đơn giản nhất: process đến trước được phục vụ trước. Dùng FIFO queue.
Ví dụ minh hoạ
Process Burst time
P1 24
P2 3
P3 3
Order: P1 → P2 → P3
Gantt: |---------- P1 ----------|P2|P3|
0 24 27 30
Waiting time:
P1: 0
P2: 24
P3: 27
Avg waiting: (0+24+27)/3 = 17
Vấn đề: Convoy Effect
Nếu P1 chạy lâu (24), P2 và P3 phải đợi rất lâu — dù chúng nhanh. Toàn bộ "đoàn xe" mắc kẹt sau 1 xe chậm. Nếu thay đổi thứ tự: P2 → P3 → P1 → avg waiting = (0+3+6)/3 = 3 (giảm 5.7×).
- Ưu: đơn giản, fair theo thứ tự đến
- Nhược: convoy effect, response time tệ
- Preemptive? Không
4.5 SJF — Shortest Job First (và SRTF)
Chọn process có burst time ngắn nhất chạy trước.
Process Burst time
P1 24
P2 3
P3 3
Order: P2 → P3 → P1
Gantt: |P2|P3|--------- P1 ---------|
0 3 6 30
Waiting time:
P2: 0, P3: 3, P1: 6
Avg waiting: (0+3+6)/3 = 3
SJF tối ưu lý thuyết — cho average waiting time thấp nhất. Nhưng có 2 vấn đề thực tế:
- Không biết burst time tương lai! Phải dự đoán dựa trên lịch sử (exponential averaging).
- Starvation: nếu liên tục có process ngắn đến, process dài có thể không bao giờ chạy.
SRTF — Shortest Remaining Time First (preemptive SJF)
Preemptive version: process mới đến có remaining time ngắn hơn process hiện tại → preempt ngay.
4.6 Priority Scheduling + Aging
Mỗi process gắn 1 số priority. Scheduler chọn process có priority cao nhất.
Process Burst Priority (smaller = higher)
P1 10 3
P2 1 1 ← highest
P3 2 4
P4 1 5
P5 5 2
Order: P2 → P5 → P1 → P3 → P4
Gantt: |P2|----- P5 -----|------ P1 ------|--P3--|P4|
Starvation và Aging
Process priority thấp có thể không bao giờ chạy nếu liên tục có process priority cao. Starvation.
Aging: tăng priority dần theo thời gian process đợi. Sau đủ lâu, dù priority ban đầu thấp, nó sẽ leo lên cao và được chạy.
Linux nice value
# Tạo process với priority thấp (nice +10, "tử tế hơn")
nice -n 10 ./long_running_task
# Đổi priority của process đang chạy
renice -n 5 -p 1234
# Xem nice value
ps -eo pid,ni,comm | head
Linux nice: -20 (cao nhất) đến +19 (thấp nhất). User thường: 0 đến +19. Cần root để đặt nice âm.
4.7 Round Robin (RR) — fair time slicing
Mỗi process được CPU 1 khoảng time quantum (vd 10ms). Hết quantum, OS preempt và chuyển sang process tiếp theo trong queue (FIFO). Process bị preempt được đẩy về cuối queue.
Process Burst time Quantum = 4ms
P1 24
P2 3
P3 3
Gantt:
|P1|P2|P3|P1|P1|P1|P1|P1|
0 4 7 10 14 18 22 26 30
Waiting time:
P1: (10-4) = 6 (chỉ tính lần đầu đợi)
P2: 4
P3: 7
Avg: (6+4+7)/3 = 5.67
Time quantum — chọn thế nào?
- Quá nhỏ (vd 1ms): nhiều context switch → overhead cao, throughput thấp
- Quá lớn (vd 1s): RR thoái hoá thành FCFS, response time tệ
- Vừa phải (10-100ms): cân bằng — Linux mặc định ~6ms với CFS
- Ưu: fair, response time tốt
- Nhược: không tối ưu turnaround/waiting; mọi process được đối xử như nhau (không xét priority hay loại workload)
4.8 MLFQ — Multi-Level Feedback Queue
Thuật toán "tự học" — kết hợp nhiều RR queue với priority khác nhau, và tự điều chỉnh priority dựa trên hành vi process.
Quy tắc MLFQ
- Process mới → vào queue cao nhất (Q0)
- Scheduler chọn process từ queue cao nhất không rỗng
- Process dùng hết quantum mà chưa xong → đẩy xuống queue thấp hơn (CPU-bound)
- Process tự nguyện block I/O trước khi hết quantum → giữ priority (I/O-bound)
- Định kỳ "boost" mọi process lên Q0 (tránh starvation)
Hiệu ứng: I/O-bound process (text editor, terminal) ở Q cao → response nhanh. CPU-bound process (build, render) "rơi" xuống Q thấp → vẫn chạy nhưng không chiếm hết CPU.
MLFQ là tổ tiên của các scheduler hiện đại. Windows NT, BSD đều dùng biến thể MLFQ.
4.9 Linux CFS — Completely Fair Scheduler
Linux từ 2.6.23 (2007) dùng CFS. Ý tưởng: mô phỏng "ideal multitasking" — nếu có N process,
mỗi process đáng lẽ phải nhận 1/N CPU time. CFS theo dõi vruntime (virtual runtime) của mỗi process,
chọn process có vruntime nhỏ nhất để chạy.
Cấu trúc dữ liệu
CFS dùng Red-Black Tree sắp xếp process theo vruntime. Lấy min = O(log n).
Cách vruntime tăng
Process chạy 10ms thực tế → vruntime tăng 10ms × hệ số (phụ thuộc nice value). Nice cao (priority thấp) → vruntime tăng nhanh hơn → ít được chạy.
Các tunable
# Xem các CFS parameter
cat /proc/sys/kernel/sched_min_granularity_ns # min slice ~1.5ms
cat /proc/sys/kernel/sched_latency_ns # target latency ~6ms
cat /proc/sys/kernel/sched_wakeup_granularity_ns
# Xem scheduling stats của 1 process
cat /proc/$$/sched | head
CFS không có "queue" theo priority. Tất cả process trong 1 cây cân bằng theo vruntime. Priority chỉ ảnh hưởng tốc độ vruntime tăng. Đơn giản nhưng cực hiệu quả.
Real-time scheduler (SCHED_FIFO, SCHED_RR)
Cho process cần độ trễ thấp (audio, robot control), Linux có scheduling class riêng:
SCHED_FIFO (FCFS, không preempt cho đến khi block) và SCHED_RR (RR với time quantum).
Cần root: chrt -f 99 ./realtime_app.
Bài tập
4 process: P1=8, P2=4, P3=9, P4=5 (burst time, đến cùng lúc t=0). Tính avg waiting cho FCFS, SJF, RR (quantum=4).
Trong FCFS, "convoy effect" là gì? Cho ví dụ cụ thể (2 process). Cách khắc phục?
Chạy top -d 0.5, để 1 minute. Quan sát process nào chạy nhiều CPU. Với nice -n 19 yes > /dev/null &,
so sánh CPU% với yes > /dev/null & (nice 0).
Quantum 1ms vs 100ms vs 1s — lợi/hại từng cái? Đa số OS chọn quantum bao nhiêu?
Cho ví dụ pure priority scheduling gây starvation. Aging giải quyết thế nào?
Trong CFS, process A có nice -5 và process B có nice 5. Nếu cả 2 cùng CPU-bound, CPU time tỉ lệ như nào? Hint: nice diff 1 = ~10% CPU diff theo Linux source.
🧪 Quiz cuối chương
Câu 1. Convoy effect xảy ra ở scheduler nào?
Đáp án: FCFS. Trong FCFS, mọi process sau bị đợi process dài chạy xong, dù chính chúng rất ngắn.
Câu 2. SJF tối ưu cho metric nào?
Đáp án: avg waiting time. SJF chứng minh được tối ưu lý thuyết. Nhược: không biết burst trước, có starvation.
Câu 3. Aging được dùng để giải quyết vấn đề nào?
Đáp án: Starvation. Tăng priority dần theo thời gian đợi → cuối cùng cũng được chạy.
Câu 4. Round Robin với quantum quá NHỎ thì sao?
Đáp án: nhiều context switch. Mỗi switch tốn ~vài microsecond. Quantum 0.1ms → 90% thời gian là switch.
Câu 5. Linux dùng scheduler chính nào?
Đáp án: CFS. Linux từ 2.6.23 (2007). Cấu trúc Red-Black tree, lấy min vruntime.
Câu 6. Trong CFS, process có nice value cao (vd +19) sẽ?
Đáp án: ít CPU hơn. Nice cao = priority thấp = "tử tế" với process khác.
Câu 7. Preemptive scheduling cần phần cứng gì?
Đáp án: timer interrupt. Không có timer, process tự nguyện trả CPU mới được chạy → cooperative.
Câu 8. MLFQ "tự học" gì?
Đáp án: CPU-bound vs I/O-bound. Process dùng hết quantum → CPU-bound → tụt priority. Process tự block I/O sớm → I/O-bound → giữ priority cao.
Tổng kết chương 4
- ✅ Scheduler = bộ não chọn process nào dùng CPU; chạy hàng nghìn lần/giây
- ✅ 6 metric đánh giá: utilization, throughput, turnaround, waiting, response, fairness — thường mâu thuẫn
- ✅ Preemptive (cần timer interrupt) là chuẩn của OS hiện đại
- ✅ FCFS: convoy effect; SJF: optimal lý thuyết, có starvation
- ✅ Priority + Aging: tránh starvation
- ✅ Round Robin: fair với time quantum, response tốt
- ✅ MLFQ: tự học CPU-bound vs I/O-bound
- ✅ Linux CFS: vruntime + Red-Black tree, không có "queue", priority qua nice