📚 第一章 网络基础知识
📄 一句话概括

分时系统把 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 秒实验

  1. 把任务数从 3 个改成 30 个(每个 30 毫秒),看最差响应怎么变——任务数比片长影响更大。
  2. 给 T3 加优先级(每次轮到它连跑两片),看最差响应是变好还是变坏。
  3. 把片长设成 1 毫秒,算切换次数,再假设每次切换 10 微秒,算开销占比。

常见误区

  1. 以为分时就是并行。 并发 ≠ 并行:单核上同一时刻只有一个任务在真跑, 分时只是切得快,制造出同时在跑的错觉。
  2. 以为时间片越小越好。 切换有开销,片小到接近切换开销时,大部分 CPU 都在做切换, 不是干活。
  3. 以为分时系统解决了调度问题。 它只解决了「公平」, 优先级、饥饿、实时性都还得另外设计。
自测三问(点开对答案) 1. 时间片 20 毫秒、3 个任务各 300 毫秒,一共切换多少次? —— **45 次**。总工作量 900 毫秒 ÷ 20 毫秒 = 45。**片长减半,切换次数翻倍,这是线性关系。** 2. 并发和并行的区别是什么? —— **并发是「看起来同时」,并行是「真的同时」。**单核多任务靠快速切换实现并发, 多核才能真正并行。 3. 为什么时间片不能设得太小? —— **因为每次切换都要保存现场、换栈、刷缓存**,这些都是纯开销。 片小到与开销同量级时,**CPU 大部分时间在切换而不是干活。**

下一节扩到多台机器:一台机器分给多人还不够,人们开始想让多台机器互相说话。

相关笔记