分时系统把 CPU 时间切成固定长度的「片」,轮流发给每个任务。 片越短响应越快,但切换次数越多;片太长又退化成批处理。
1.2.1 的批处理输在「不响应」,这一节就是针对这一点开的药方。 1.2.3 会把范围从「一台机器分给多人」扩到「多台机器互相通信」——网络登场。
一、时间片是什么
时间片(time slice)就是「一次最多让你跑多久」。用完就换下一个,不管你跑完没跑完。
| 概念 | 含义 | 典型值 |
|---|---|---|
| 时间片 | 一次连续占用 CPU 的上限 | 10~100 毫秒 |
| 轮转 | 所有就绪任务排队,依次发片 | 同优先级 |
| 上下文切换 | 换任务时保存现场、装新现场 | 微秒级 |
第三行那个「微秒级」是本机测不出来的——它发生在内核里,用户态程序看不到。 但它的影响是实打实的: 片长 10 毫秒、切换开销 10 微秒,开销占比 0.1%;片长缩到 100 微秒,占比就升到 10%。 所以「片不能无限小」不是保守,是算出来的。
二、响应时间怎么估
最差响应时间有个粗略上限:排在最后那个任务,要等前面所有任务各跑一片。
| 任务数 | 片长 | 最差响应(估算) |
|---|---|---|
| 3 | 20 毫秒 | 40 毫秒 |
| 3 | 100 毫秒 | 200 毫秒 |
| 30 | 20 毫秒 | 580 毫秒 |
第三行说明一件重要的事:任务数一多,再短的片也救不了响应时间。 这时候就要靠「优先级」——交互型任务给高优先级,后台任务给低优先级。 这也是为什么现代操作系统里「实时任务」要单独一套调度策略。
三、它和「并行」不是一回事
这一点最容易被混淆:
| 概念 | 同一时刻 | 例子 |
|---|---|---|
| 并发 | 只有一个在真跑 | 单核上的多任务 |
| 并行 | 多个同时在真跑 | 多核、多机 |
第一行那个「只有一个在真跑」是并发最早的样子: 分时系统靠「切得快」制造出「同时在跑」的错觉。 人的感知阈值大概在 100 毫秒——切得比这更快,人就看不出断点。 所以「分时」这件事的本质不是技术,是感知:它骗过了人的时间感。 今天所有的「多任务」都还在这条路上,只是切的对象从 CPU 扩到了网络、磁盘、GPU。
四、动手 Demo:分时系统的时间片怎么切
3 个任务,每个要 300 毫秒,总工作量 900 毫秒。用三种片长(20/50/100 毫秒)各跑一遍, 统计切换次数与「最差响应」——最差响应指最后一个任务第一次拿到 CPU 的时刻。
tasks = [('T1', 300), ('T2', 300), ('T3', 300)]
for slice_ms in (20, 50, 100):
left = {n: d for n, d in tasks}
clock = 0
first = {}
switches = 0
while any(v > 0 for v in left.values()):
for n, _d in tasks:
if left[n] <= 0:
continue
if n not in first:
first[n] = clock # 第一次拿到 CPU 的时刻
use = min(slice_ms, left[n])
left[n] -= use
clock += use
switches += 1
print('片%3d 切换%3d 最差响应%4d 首次 %s'
% (slice_ms, switches, max(first.values()),
'/'.join(str(first[k]) for k in ('T1', 'T2', 'T3'))))
实跑输出:
--------------------------------------------------------------------
分时系统的时间片怎么切
--------------------------------------------------------------------
**分时系统把 CPU 时间切成固定长度的「片」,轮流发给每个任务。**
**片越短,响应越快;但切的次数越多,切换本身的开销占比越高。**
时间片ms 切换次数 最差响应ms 三者首次响应
------------------------------------------------------
20 45 40 0/20/40
50 18 100 0/50/100
100 9 200 0/100/200
**900 毫秒的总工作量不变,片越小「最差响应」越短。**
**但表里没算切换开销**——每切一次要保存现场、换栈、刷缓存,
这个开销本机测不出来(真实调度器在毫秒级以下)。书上的结论是:片不能太小,一般取 10~100 毫秒。
**本机能算的只有「切多少次」,切一次的代价这次没测。**
切换次数正好是「总工作量 ÷ 片长」:900÷20=45、900÷50=18、900÷100=9。 这三个数与表里一模一样,说明这个模型里没有浪费——每一毫秒都在干活。 而真实系统里切换要花时间,所以真实切换次数一样、但总时间会变长。 这个开销本机测不出来,因为它是内核态的事——所以这里只给「切多少次」,不给「切一次多贵」。
30 秒实验
- 把任务数从 3 个改成 30 个(每个 30 毫秒),看最差响应怎么变——任务数比片长影响更大。
- 给 T3 加优先级(每次轮到它连跑两片),看最差响应是变好还是变坏。
- 把片长设成 1 毫秒,算切换次数,再假设每次切换 10 微秒,算开销占比。
常见误区
- 以为分时就是并行。 并发 ≠ 并行:单核上同一时刻只有一个任务在真跑, 分时只是切得快,制造出同时在跑的错觉。
- 以为时间片越小越好。 切换有开销,片小到接近切换开销时,大部分 CPU 都在做切换, 不是干活。
- 以为分时系统解决了调度问题。 它只解决了「公平」, 优先级、饥饿、实时性都还得另外设计。
自测三问(点开对答案)
1. 时间片 20 毫秒、3 个任务各 300 毫秒,一共切换多少次? —— **45 次**。总工作量 900 毫秒 ÷ 20 毫秒 = 45。**片长减半,切换次数翻倍,这是线性关系。** 2. 并发和并行的区别是什么? —— **并发是「看起来同时」,并行是「真的同时」。**单核多任务靠快速切换实现并发, 多核才能真正并行。 3. 为什么时间片不能设得太小? —— **因为每次切换都要保存现场、换栈、刷缓存**,这些都是纯开销。 片小到与开销同量级时,**CPU 大部分时间在切换而不是干活。**下一节扩到多台机器:一台机器分给多人还不够,人们开始想让多台机器互相说话。
- ☐ (本节完,下一节 1.2.3 计算机之间的通信)