2026计算机学科专业基础综合全卷预览

2026年408真题

2026年408真题完整试卷,包含 40 道选择题和 7 道综合应用题,共 47 题,题目分值合计 150 分。可按目录阅读全卷,展开参考答案解析,或进入对应题目练习。

返回年度练习
47题全卷题目
150分题目分值合计
40题选择题 · 80分
7题综合应用题 · 70分

选择题

40 题 · 共 80 分

第 1 题

数据结构
2 分

当存储空间有足够的空闲空间时,在保持表内元素顺序相对不变的情况下,下列哪些操作会必然导致产生移动次数()。

I. 表头插入一个元素

II. 表头删除一个元素

III. 表尾插入一个元素

IV. 表尾删除一个元素

A. I、II
B. I、III
C. II、IV
D. III、IV

查看答案与解析收起答案与解析

参考答案:A

核心:顺序表把元素紧密连续存放在数组下标 0…length-1 上。当向第 i 位插入/删除时,必须把 [i, length-1] 这段元素整体后移或前移一格,移动的元素数 = 受影响区间的长度。

逐个核对:

  • I 表头插入:插入位置 i = 0,需把 [0, length-1] 全部后移一格 → 移动 length 次。必然移动。
  • II 表头删除:删除位置 i = 0,需把 [1, length-1] 全部前移一格 → 移动 length-1 次。必然移动。
  • III 表尾插入:插入位置 i = length,新元素直接写到 a[length],length++ → 0 次移动。
  • IV 表尾删除:直接 length-- 即可,原 a[length-1] 被"忽略"(下次写入时被覆盖)→ 0 次移动。

所以"必然产生移动"的是 I 和 II。

最终答案是 A。

进入练习

第 2 题

数据结构
2 分

设有一个双向链表 L,结构为 [p2, p1],头结点为 head。初始时 head = cu。现要将每个结点的 p2 指向 p1 指向结点的直接后继,应该进行的操作是()。

A. while(cu!=NULL) {cu->p2=cu->p1->p1; cu=cu->p1;}
B. while(cu!=NULL && cu->p2!=NULL) {cu->p2 = cu->p1->p1; cu = cu->p1;}
C. while(cu!=NULL) {if(cu->p1!=NULL) {cu->p2=cu->p1->p1; cu=cu->p1;}}
D. while(cu!=NULL) {if(cu->p1!=NULL) {cu->p2=cu->p1->p1;} else {cu->p2=NULL;} cu=cu->p1;}

查看答案与解析收起答案与解析

参考答案:D

先读懂题:每个结点有两个指针字段 p1 和 p2,其中 p1 是双向链表的"指向直接后继"指针(即 next),p2 是要被赋值的辅助指针。需求是 cu->p2 = cu->p1->p1,即让 p2 指向"下下个结点"。

关键边界:当 cu 是尾结点时,cu->p1 == NULL,没有"下下个结点",应令 cu->p2 = NULL。

正确实现必须同时满足三点:

  1. 进入 cu->p1->p1 之前必须判断 cu->p1 != NULL,否则空指针崩溃;
  2. 尾结点不能跳过——它的 p2 应被显式置为 NULL;
  3. cu = cu->p1 必须放在 if/else 之外(即不论是否进入 if,都要推进),否则尾结点上死循环。

逐项核对:

  • A ✗ 缺第 1 条边界检查,尾结点会触发空指针解引用。
  • B ✗ 把判断字段误用为 p2,并且把"应该被赋值"的结点提前放过去了。
  • C ✗ 满足第 1 条,但违反第 2、3 条——推进语句被困在 if 里,且尾结点 p2 未置 NULL。
  • D ✓ 三条都满足:if 里赋值"下下个",else 把尾结点 p2 置 NULL,cu = cu->p1 在 if/else 之外,能在尾结点这一轮做完赋值后把 cu 推进到 NULL,正常退出。

最终答案是 D。

编者注(生僻术语):题干 "结构为 [p2, p1]" 是原题对结点字段的简写表示(结点中含 p2 和 p1 两个指针字段);"初始时 head = cu" 在 C 语义里赋值方向看似反了,但按上下文应理解为"用 cu 从 head 开始遍历"——本题保留原题原文,不做改动。p1 实际承担常规的"指向直接后继 (next)"职责,p2 是题目要求的辅助指针。

进入练习

第 3 题

数据结构
2 分

已知二叉树 T 的中序遍历为 b, e, d, f, c, a, g,层序遍历为 a, b, g, c, d, e, f,则其后序遍历序列为()。

A. c, e, d, f, b, g, a
B. c, e, f, d, b, g, a
C. e, f, d, c, b, g, a
D. e, g, f, d, b, c, a

查看答案与解析收起答案与解析

参考答案:C

重建步骤:

Step 1:层序首位是根,故 根 = a。在中序里以 a 切分:b, e, d, f, c | a | g。左子树中序 = b, e, d, f, c;右子树中序 = g。

Step 2:右子树中序只剩 g,所以右子树 = 单结点 g。

Step 3:在层序剩余 b, g, c, d, e, f 中,第一个属于左子树(即左子树根)的是 b。中序里 b 在最左 → b 没有左子树;右子树中序 = e, d, f, c。

Step 4:层序中接下来属于 b-右子树的第一个是 c(g 已被右子树拿走,c 在 d/e/f 之前)。在中序 e, d, f, c 里 c 在最右 → c 没有右子树;左子树中序 = e, d, f。

Step 5:层序里接下来出现的是 d(在 e、f 之前)。在中序 e, d, f 里 d 居中 → d 的左孩子 = e,右孩子 = f。

重建出的二叉树:

binary-tree 复制代码
a(b(_, c(d(e, f), _)), g)

后序遍历(左-右-根)按子树分块:

  • d 子树:e, f, d
  • c 子树:(d 子树), c → e, f, d, c
  • b 子树:(c 子树), b → e, f, d, c, b
  • a 子树:(b 子树), g, a → e, f, d, c, b, g, a

最终答案是 C(e, f, d, c, b, g, a)。

进入练习

第 4 题

数据结构
2 分

森林 F 中有 5 颗树,其节点个数分别为 2、3、4、5、7,森林中树的次序可以任意,问 F 对应的二叉树最小高度为多少?

A. 5
B. 6
C. 8
D. 10

查看答案与解析收起答案与解析

参考答案:B

关键事实 1(单棵树):森林转二叉树用孩子兄弟法——单棵 m 节点树转出的二叉树根没有右孩子(因为根没有兄弟),左子树包含其余 m-1 个节点。这左子树是任意 m-1 节点的二叉树,原树结构可自选 → 左子树最小高度 = ⌈log⁡2m⌉\lceil \log_2 m \rceil。所以单棵 m 节点树转二叉树的最小内部高度

h(m)=1+⌈log⁡2m⌉h(m) = 1 + \lceil \log_2 m \rceil

代入题中 5 个节点数:

m 2 3 4 5 7
h(m) 2 3 3 4 4

关键事实 2(多棵树串联):森林 T1,T2,…,TkT_1, T_2, \dots, T_k 转二叉树,TiT_i 的根处于二叉树的层 ii(第一棵的根在层 1,第二棵的根在层 2 即第一棵根的右孩子,依次类推)。所以每棵树对总高度的"贡献" = (i−1)+h(mi)(i-1) + h(m_i),森林二叉树总高度

H=max⁡i=1..k[(i−1)+h(mi)]H = \max_{i=1..k} \big[(i-1) + h(m_i)\big]

关键事实 3(排序使 max 最小):要让 max 最小,把 hh 大的树放到 ii 小的位置(贪心地"先消耗大头")。本题 hh 列表 = {4, 4, 3, 3, 2},降序排到位置 1…5:

位置 i 1 2 3 4 5
h 4 4 3 3 2
(i-1)+h 4 5 5 6 6

最大值 = 6。

(验证一下别的排法:升序 2,3,3,4,4 给出 max = 2,4,5,7,8 → 8;中间打乱也不会比 6 更小。)

最终答案是 B。

进入练习

第 5 题

数据结构
2 分

假设二叉树中节点权值为 a=1, b=2, c=4, d=5, e=8, f=10, g=12。当带权路径长度 (WPL) 最小时,与节点 e(权值 8)处于相同深度的节点是哪些?

A. d
B. g
C. d, f
D. f, g

查看答案与解析收起答案与解析

参考答案:D

哈夫曼构造(每次取最小两数合并,把它们作为新结点的左右孩子):

步 取出两数 合并出 当前森林中的根(权值)
0 — — 1(a), 2(b), 4(c), 5(d), 8(e), 10(f), 12(g)
1 1, 2 3[ab] 3, 4(c), 5(d), 8(e), 10(f), 12(g)
2 3, 4 7[abc] 5(d), 7, 8(e), 10(f), 12(g)
3 5, 7 12[abcd] 8(e), 10(f), 12, 12(g)
4 8, 10 18[ef] 12, 12(g), 18
5 12, 12 24[abcdg] 18, 24
6 18, 24 42[根] 42

画出最终哈夫曼树(根记为深度 0):

复制代码
                    42
                  /    \
                18      24
               /  \    /  \
              e    f  12   g
                       /\
                      d  7
                         /\
                        3  c
                       /\
                      a  b

逐叶子核对深度:

叶子 权值 深度
a 1 5
b 2 5
c 4 4
d 5 3
e 8 2
f 10 2
g 12 2

与 e 同深度(深度 2)的叶子是 f 和 g。

最终答案是 D。

进入练习

第 6 题

数据结构
2 分

有向图 G=(V, E) 采用邻接表存储,求某点入度的时间复杂度为()。

A. O(|V|)
B. O(min(|V|, |E|))
C. O(|E|)
D. O(max(|V|, |E|))

查看答案与解析收起答案与解析

参考答案:D

邻接表的存储结构:一个长度为 ∣V∣|V| 的头指针数组,每个表头挂出该顶点的出边链表。问题在于:邻接表只直接记录出边,入边没有单独表(除非额外维护逆邻接表)。

求顶点 v 的入度:

  1. 对每个顶点 i = 1..|V|,遍历 i 的出边链表;
  2. 凡看到一条 (i, v) 的边,入度计数器 +1;
  3. 扫完所有顶点的链表后得到入度。

开销分析(必须把两部分都算上):

  • 遍历所有 ∣V∣|V| 个头指针:O(∣V∣)O(|V|);
  • 遍历所有 ∣E∣|E| 条边:O(∣E∣)O(|E|)。

合计 O(∣V∣+∣E∣)O(|V| + |E|),写成等价形式 O(max⁡(∣V∣,∣E∣))O(\max(|V|, |E|))。

为什么 C 不严谨:在稠密或一般连通图中 ∣E∣≥∣V∣|E| \ge |V|,此时 O(∣E∣)=O(max⁡(∣V∣,∣E∣))O(|E|) = O(\max(|V|, |E|)),C 和 D 渐近等价;但没有这个假设时 D 才是紧的上界——比如一个只有几条边的稀疏图,扫描 ∣V∣|V| 个头指针的代价就主导了。

最终答案是 D。

进入练习

第 7 题

数据结构
2 分

设有向图 G=(V, E),顶点集大小 n=|V|,每条边标记一个字符。定义字符串集 S 为所有路径上边标记拼接成的字符串的集合。以下说法错误的是()。

A. 若 G 无环,则 S 是有限集
B. 若 G 无环,则 S 中存在长度为 n 的字符串
C. 若 G 有环,则 S 中存在长度大于 n 的字符串
D. 若 G 有环,则 S 中存在长度小于 2n 的字符串

查看答案与解析收起答案与解析

参考答案:B

先把"路径"读清楚:本题中 S 包含所有路径对应的边标记字符串,路径可以重复顶点(这是允许讨论"绕环长度"的前提)。

两种情形分别分析:

Case 1:G 无环

  • 任何路径都不能重复顶点(重复就成环了)→ 这就是简单路径;
  • 简单路径最多覆盖 n 个不同顶点 → 最多 n−1 条边;
  • 字符串长度上限 n−1,不存在长度恰为 n 的字符串;
  • 简单路径数上界 ≤ n!,S 有限。
  • 故 A ✓,B ✗(B 是错误说法)。

Case 2:G 有环

  • 可沿环绕任意多圈,每圈把字符串延长一段固定子串 → 字符串长度可任意大,必然存在长度 > n 的字符串。C ✓。
  • 有环 → 至少有边 → 单条边即为长度 1 的字符串,1 < 2n。D ✓。

结论:错误的说法只有 B。

最终答案是 B。

进入练习

第 8 题

数据结构
2 分

已知平衡二叉树 (AVL 树) 高度为 4(根节点高度记为 1),则其根节点的左右子树的节点数之差最多为()。

A. 1
B. 2
C. 3
D. 5

查看答案与解析收起答案与解析

参考答案:D

条件回顾:AVL 树高度 4(根算第 1 层),求根的左右子树节点数差的最大值。

思路:让一棵子树尽量胖、另一棵尽量瘦。

Step 1:高度约束

整棵树高度 4 → 至少有一棵子树高度 3(否则整树高度只到 3)。AVL 平衡条件允许另一棵子树高度比这棵差 1,即高度 2。

最佳分配:胖的子树高 3,瘦的子树高 2。

Step 2:胖子树最多节点

高度 3 的二叉树最多节点 = 满二叉树 = 23−1=72^3 - 1 = \mathbf{7}。

Step 3:瘦子树最少节点(AVL 高度 h 最少节点的递推)

N(h)=N(h−1)+N(h−2)+1,N(1)=1, N(2)=2N(h) = N(h-1) + N(h-2) + 1, \quad N(1) = 1, \ N(2) = 2

h 1 2 3 4
N(h) 1 2 4 7

高度 2 的最少节点 = N(2) = 2。

Step 4:节点数之差

最大差=7−2=5\text{最大差} = 7 - 2 = \mathbf{5}

举例验证(一棵高度 4 的合法 AVL):

复制代码
                root
               /    \
            (高3满)  (高2最少)
              7节点    2节点

整棵树高度 4,根的左右子树高度差 |3-2|=1 ≤ 1(AVL 合法),节点差 5。

最终答案是 D。

进入练习

第 9 题

数据结构
2 分

使用直接插入排序对序列进行升序排序,以下比较次数最少的是()。

A. 30, 27, 56, 41, 80, 95, 69
B. 31, 43, 26, 55, 63, 99, 77
C. 61, 84, 51, 23, 34, 91, 40
D. 93, 32, 48, 81, 50, 21, 72

查看答案与解析收起答案与解析

参考答案:B

核心规律:直接插入排序对第 i 个元素 (i ≥ 2),从已排序部分末尾向前比较,直到找到合适位置或越界。最好情况(已升序)每个新元素只比 1 次,总比较 n−1;最坏情况(已降序)第 i 个元素比 i 次,总比较 n(n−1)/2n(n-1)/2。

逐项数比较次数(n=7,对 a[2..7] 共 6 个元素插入):

A. 30, 27, 56, 41, 80, 95, 69

插入 已排序前缀 比较过程 次数
27 [30] 27<30 → 越界 1
56 [27,30] 56>30 1
41 [27,30,56] 41<56, 41>30 2
80 [27,30,41,56] 80>56 1
95 [27,30,41,56,80] 95>80 1
69 [27,30,41,56,80,95] 69<95, 69<80, 69>56 3

合计 9 次。

B. 31, 43, 26, 55, 63, 99, 77

插入 比较过程 次数
43 43>31 1
26 26<43, 26<31 → 越界 2
55 55>43 1
63 63>55 1
99 99>63 1
77 77<99, 77>63 2

合计 8 次。✓ 最少。

C. 61, 84, 51, 23, 34, 91, 40

逐项算:1 + 2 + 3 + 4 + 1 + 5 = 16 次。

D. 93, 32, 48, 81, 50, 21, 72

逐项算:1 + 2 + 2 + 3 + 5 + 3 = 16 次。

汇总:A=9,B=8,C=16,D=16。

观察 B 序列的结构:除了 26 要插到最前(贡献 2 次比较)和 77 略往前插(2 次),其余都是后一个数大于已排序末尾,每次只 1 次比较——B 序列"基本升序"的程度最高。

最终答案是 B。

进入练习

第 10 题

数据结构
2 分

现有 nn 名学生的成绩记录,每位学生的记录包含两门课程的成绩:课程 1(记为 C1C_1)和课程 2(记为 C2C_2)。

排序规则如下:

  1. 首先,依据 C1C_1 成绩升序排列;
  2. 若两名学生的 C1C_1 成绩相同,则依据其总分(即 C1+C2C_1 + C_2)升序排列。

请从下列排序算法中,选择最适合实现上述需求的算法()。

A. 基数排序
B. 快速排序
C. 希尔排序
D. 选择排序

查看答案与解析收起答案与解析

参考答案:A

多关键字排序的标准做法:基数排序(LSD,Least Significant Digit 最低位优先)。

具体到本题,主关键字是 C1C_1、次关键字是总分 C1+C2C_1 + C_2,按 LSD 思路实现:

  1. 第一趟:以"总分"为关键字,对所有记录做稳定排序;
  2. 第二趟:以"C1C_1"为关键字,对上一趟结果再做稳定排序。

为什么这样得到的结果正确:第二趟按 C1C_1 排,稳定性保证当 C1C_1 相同时,原有的相对顺序不变——而原有顺序正是第一趟排好的总分升序。两遍稳定排序合起来恰好满足"主关键字 C1C_1 升序、C1C_1 相同时总分升序"。

其他三个算法的问题:

算法 是否稳定 是否原生支持多关键字
基数排序 ✓ 稳定 ✓ LSD 思想
快速排序 ✗ 不稳定 ✗ 单关键字
希尔排序 ✗ 不稳定 ✗ 单关键字
选择排序 ✗ 不稳定 ✗ 单关键字

虽然 B/C/D 通过改写比较函数也能完成任务,但题目问的是**"最适合"**——基数排序是教材里专为多关键字排序设计的算法。

最终答案是 A。

进入练习

第 11 题

数据结构
2 分

在外部排序的 kk 路归并过程中,归并趟数为 dd。下列关于 kk、dd、初始归并段及内存大小的说法中,正确的是()。

Ⅰ. kk 越大,dd 越小

Ⅱ. 初始归并段数不影响 dd

Ⅲ. 内存大小限制初始归并段的最大长度

A. 仅 I
B. 仅 I、II
C. 仅 I、III
D. 仅 II、III

查看答案与解析收起答案与解析

参考答案:C

外排序两阶段:

  1. 生成初始归并段:每次把内存能装下的一批记录读进来,内排序后写回外存,得到一个有序段。重复直到所有记录处理完,共得到 mm 个初始段;
  2. k 路归并:每趟把 k 个段合并成 1 个,反复进行直到只剩 1 个段。趟数

d=⌈log⁡km⌉d = \lceil \log_k m \rceil

逐项核对:

  • Ⅰ. kk 越大,dd 越小 ✓
    d=⌈log⁡km⌉d = \lceil \log_k m \rceil,对数底数 kk 增大 → 对数值减小(m 固定时),所以 d 单调减少。

  • Ⅱ. 初始归并段数不影响 dd ✗
    公式里 mm 直接进入 dd。mm 越大,log⁡km\log_k m 越大 → d 越大。比如 k=4k=4、m=16m=16 时 d=2d=2;m=64m=64 时 d=3d=3。

  • Ⅲ. 内存大小限制初始归并段的最大长度 ✓
    生成初始段时,一次只能把内存能容纳的记录放进来排序,所以每个初始段的长度上限 = 内存容纳记录数。内存越大,初始段越长,需要的初始段数 mm 也越少(间接也让 d 变小)。

正确的说法是 I 和 III。

最终答案是 C。

进入练习

第 12 题

计算机组成原理
2 分

下列关于计算机的系统层次的叙述,错误的是( )。

A. 最上层是应用软件层
B. 指令集体系结构是软件和硬件的接口
C. 计算机组成(即微架构)属于指令集体系结构的物理实现层
D. 操作系统可通过 ISA 进行抽象,向上层软件提供服务

查看答案与解析收起答案与解析

参考答案:C

题目考点:计算机系统的多层抽象模型,重点在 ISA 与 微架构 / 物理实现 这两层之间的关系。

层次模型速查(自顶向下):

层次 角色
应用程序 用户直接使用的软件
高级语言 / 编译器 把 C / Java 翻译成汇编
汇编语言 汇编器翻译成机器码
操作系统 在 ISA 之上做资源管理与抽象
ISA(指令集体系结构) 软硬件的接口——指令格式、寻址方式、寄存器模型
微架构(计算机组成) ISA 的逻辑实现——数据通路、控制器、流水线等
逻辑电路 门电路、组合 / 时序逻辑
物理实现 晶体管、版图、工艺

逐项判断:

选项 是否正确 理由
A ✓ 应用软件层确实是最顶层
B ✓ ISA 是软硬件接口的标准定义
C ✗ 微架构是 ISA 的逻辑实现层(数据通路、控制器层面),不是物理实现层——"物理实现"特指更底层的电路、晶体管、版图,比微架构还要低两层
D ✓ OS 在 ISA 之上构建抽象,向应用提供系统调用

C 错在哪:把"微架构"和"物理实现层"画了等号。微架构回答的是"用什么样的数据通路 / 控制器去执行 ISA"(属于逻辑层面),而物理实现回答的是"用什么晶体管、什么工艺、什么版图把这些电路造出来"(属于物理层面)。同一套 ISA 可以有多种微架构(比如 x86 的奔腾系列 vs 酷睿系列),同一种微架构也可以由不同物理工艺生产——两层的关注点完全不同,不能混为一谈。

最终答案是 C。

进入练习

第 13 题

计算机组成原理
2 分

对机器数 1010 0110B 先执行算术右移 3 位,再执行算术左移 2 位,最终结果是( )。

A. 1101 0000B
B. 1101 0011B
C. 0101 0000B
D. 0101 0011B

查看答案与解析收起答案与解析

参考答案:A

前置概念——算术移位的补位规则:

移位方向 高位补什么 低位补什么 挤出位
算术右移 复制符号位(保持正负不变) — 直接丢弃
算术左移 — 补 0 直接丢弃

原数 1010 0110B,符号位为 1(表示负数)。

第一步:算术右移 3 位

每次右移高位补 1(符号位),低位 1 位被挤掉:

操作 结果
初始 1010 0110
右移 1 位 1101 0011
右移 2 位 1110 1001
右移 3 位 1111 0100

得到 1111 0100B。

第二步:算术左移 2 位

每次左移低位补 0,高位 1 位被挤掉:

操作 结果
初始 1111 0100
左移 1 位 1110 1000
左移 2 位 1101 0000

得到 1101 0000B。

验证(可选,按真值检查):

原数 1010 0110B 是补码 → 真值 −128+32+4+2=−90-128 + 32 + 4 + 2 = -90。
算术右移 3 位 ≈ 真值 ÷ 8 = −90/8=−11.25-90 / 8 = -11.25,向负无穷取整为 −12,对应补码 1111 0100B ✓。
算术左移 2 位 = 真值 × 4 = −12×4=−48-12 \times 4 = -48,对应补码 1101 0000B ✓(注意若结果超出补码可表示范围会溢出,本题未溢出)。

最终答案是 A(1101 0000B)。

易错点速查:

  • 算术右移 ≠ 逻辑右移:前者高位补符号位,后者高位补 0
  • 算术左右移不可逆:被挤出去的位丢失,先右后左 ≠ 净位移右 1
  • 算术左移可能改变符号位(导致溢出),右移不会
进入练习

第 14 题

计算机组成原理
2 分

已知用 IEEE 754 单精度浮点数表示浮点型变量,采用就近舍入(中间值取偶数)。若浮点型变量 x 为 12.1,则 x 的机器数是( )。

A. 4141 9999H
B. 4141 999AH
C. 41E0 CCCCH
D. 41E0 CCCDH

查看答案与解析收起答案与解析

参考答案:B

IEEE 754 单精度格式(共 32 位):

bits-layout 复制代码
width: 32
fields:
  [31, 31] S
  [30, 23] E
  [22, 0] M
  • S:1 位符号位(0 正 1 负)
  • E:8 位阶码(移码,偏移量 127)
  • M:23 位尾数(隐含前导 1)

第一步:把 12.1 写成二进制

整数部分 12 = 1100B(4 位)。

小数部分 0.1 不能精确表示,是无限循环二进制:

运算 结果
0.1×2=0.20.1 \times 2 = 0.2 取 0
0.2×2=0.40.2 \times 2 = 0.4 取 0
0.4×2=0.80.4 \times 2 = 0.8 取 0
0.8×2=1.60.8 \times 2 = 1.6 取 1
0.6×2=1.20.6 \times 2 = 1.2 取 1
0.2×2=0.40.2 \times 2 = 0.4 取 0
⋯\cdots 循环节为 0011

所以 0.1=0.0001 1001 1001 1001 … B0.1 = 0.0001\,1001\,1001\,1001\,\ldots\,B(首位 0001 后无限循环 0011)。

合起来:12.1=1100.0001 1001 1001 1001 1001 1001 1001 … B12.1 = 1100.0001\,1001\,1001\,1001\,1001\,1001\,1001\,\ldots\,B。

第二步:规格化

把小数点左移到最高位 1 后面:

12.1=1.100 0001 1001 1001 1001 1001 1001 1001 …×2312.1 = 1.100\,0001\,1001\,1001\,1001\,1001\,1001\,1001\,\ldots \times 2^3

尾数小数点后从左数第 23 位的截止位置:

复制代码
位号:   1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 24 25 ...
尾数:   1  0  0  0  0  0  1  1  0  0  1  1  0  0  1  1  0  0  1  1  0  0  1 | 1  0  0  1  1  ...

第 23 位(最低位)是 1,第 24 位起的"被舍部分" = 1 0011 0011 ...(首位是 1,后续非零)。

第三步:就近舍入

被舍部分形态 舍入动作
首位 = 0 直接截断(向下)
首位 = 1,后续非零(> 中间值) 向上进位(最低位 + 1)
首位 = 1,后续全 0(恰好中间值) 偶数舍入:使最低位为 0

本题被舍部分 1 0011... 后续非零 → 向上进位:

1001⏟原最低 4 位+1=1010\underbrace{1001}_{\text{原最低 4 位}} + 1 = 1010

尾数最终:100 0001 1001 1001 1001 1010

第四步:拼装 32 位

字段 计算 二进制
符号位 S x = 12.1 > 0 0
阶码 E 真阶 3 + 偏置 127 = 130 1000 0010
尾数 M 上一步的进位结果 100 0001 1001 1001 1001 1010

合并:

复制代码
0 | 1000 0010 | 100 0001 1001 1001 1001 1010

按 4 位重新分组:

复制代码
0100 0001 0100 0001 1001 1001 1001 1010
=  4    1    4    1    9    9    9    A

机器数 = 4141 999AH。

最终答案是 B(4141 999AH)。

关键易错点:

  1. 12 = 1100B(4 位),不是 11100B(5 位)——位数算错全盘错
  2. 0.1 是无限循环 0001 1001 1001 ...,循环节是 0011,不是 1100
  3. 就近舍入时被舍部分是 1xxxx... 且至少一位非零 → 进位;不是简单地"看第 24 位是 1 就进"
进入练习

第 15 题

计算机组成原理
2 分

用 8 个 64M×8bit 的 DRAM 芯片按交叉编址方式构成主存储器,并与一个宽度为 64bit 的存储器总线相连。主存每次最多读写 64bit,且按字节编址。则下列地址中,与主存地址 0018 001DH 位于同一芯片中的是( )。

A. 0000 01D5H
B. 000F A020H
C. 0018 001EH
D. 0101 0011B

查看答案与解析收起答案与解析

参考答案:A

结构分析:

  • 8 个芯片,每片 64M × 8bit = 64MB,位扩展每次读 8bit
  • 64bit 总线 = 8 字节,每次访存要 8 个芯片同时各供 1 字节 → 这就是低位交叉编址的工作模式
  • 每个芯片就是一个"体",体号 = 字节地址 mod 8

地址低 3 位 = 体号(芯片号):

地址(mod 8)的位 含义
第 0 位 字节在芯片内的最低位选择
第 1–2 位 …
第 0–2 位(合起来) 芯片号(体号)

也就是说:

芯片号=字节地址 mod 8=字节地址的低 3 位\text{芯片号} = \text{字节地址} \bmod 8 = \text{字节地址的低 3 位}

目标地址 0018 001DH 的体号:

末位 D = 1101B,取最低 3 位 = 101B = 5。
所以目标地址在芯片 5。

逐项判断"低 3 位是否 = 101"(注意 D 后缀是 B 表示二进制,不是十六进制):

选项 进制 末 4 位二进制 低 3 位 体号 同体?
A H:末位 D 1101 101 5 ✓
B H:末位 0 0000 000 0 ✗
C H:末位 E 1110 110 6 ✗
D B:直接读末 4 位 0011 011 3 ✗

容量校验(顺手):

8 片 × 64MB/片 = 512MB = 2292^{29} 字节 ≈ 5.4 × 10810^8,地址需要 29 位。A、B、C 的十六进制地址均小于 0x20000000 < 2292^{29},在范围内;D 是二进制 0101 0011B = 83 字节,自然也在范围内。

最终答案是 A(0000 01D5H)。

速记口诀:

  • 低位交叉编址:相邻地址分散到不同体(高速并行)
  • 高位交叉编址:连续地址在同一体内(容量扩展)
  • 判同体:看"地址 mod 体数"是否相等,等价于看二进制最低 log⁡2体数\log_2 \text{体数} 位
进入练习

第 16 题

计算机组成原理
2 分

下列不是由指令集体系结构规定的是( )。

A. 输入输出指令
B. 采用向量中断
C. 虚拟存储管理方式
D. 指令流水线是否使用超级流水线技术

查看答案与解析收起答案与解析

参考答案:D

核心区分——ISA(软硬件接口)与微架构(硬件实现)的边界:

由 ISA 规定 由微架构(实现)决定
所有可执行指令(包括 I/O、中断、特权指令) 流水线段数、是否超级流水
寄存器命名与数量 是否乱序执行、分支预测策略
中断 / 异常处理框架(向量中断 / 中断号) Cache 的具体组织(路数、容量)
内存模型与地址翻译机制(虚存机制) 物理 Cache、TLB 的具体实现
数据格式、字节序 主频、电压、工艺
寻址方式 总线宽度、I/O 控制器实现

判定法则:

软件(编译器、OS、汇编)能"看见"并依赖的接口 → ISA 规定;
同一个 ISA 上多种实现可以互相替换、对软件透明的 → 微架构决定。

逐项判断:

选项 软件可见 / ISA? 理由
A. 输入输出指令 是 I/O 指令在程序中能写、编译器要生成机器码
B. 采用向量中断 是 OS 必须知道中断如何分发,向量表结构要稳定
C. 虚拟存储管理方式 是 OS 写页表、设置 MMU 都依赖 ISA 定义的页表格式
D. 是否使用超级流水线 否 超级流水是微架构层面的实现优化,软件无感知

为什么 D 是答案:

超级流水线把 5 段流水拆成更细(如 10–20 段)以提高主频,但对软件来说,看到的还是同一套指令、同一套寄存器、同一种执行顺序——同一个 ISA 既可以用 5 段简单流水实现,也可以用 20 段超级流水实现,软件完全不需要改动。这正是微架构层面的"自由",与 ISA 无关。

最终答案是 D。

记忆抓手:

"ISA = 程序员手册里写的东西"——你查不到"我这颗 CPU 用几段流水",但能查到"有哪些指令、寄存器、中断、内存布局"。前者是微架构,后者是 ISA。

进入练习

第 17 题

计算机组成原理
2 分

哪些指令可能不改变程序下一条指令的地址?( )

Ⅰ. 条件转移

Ⅱ. 过程调用

Ⅲ. 陷入指令

Ⅳ. 返回

A. Ⅰ、Ⅱ
B. Ⅰ、Ⅳ
C. Ⅱ、Ⅲ
D. Ⅱ、Ⅳ

查看答案与解析收起答案与解析

参考答案:B

关键概念辨析——题目问的是"程序下一条指令的地址"。这里的"程序"应理解为主程序逻辑流,而不是单条指令的 PC+1。判据:执行该指令后,主程序原本约定要执行的下一条指令是否被打断 / 替换。

逐项判断:

编号 指令 执行行为 主程序原本的下一条还会被执行吗?
Ⅰ 条件转移 条件成立 → 跳转;条件不成立 → 顺序执行 PC+1 可能不变(条件不成立时)
Ⅱ 过程调用 CALL 必然跳到子程序入口,主程序流被打断 改变
Ⅲ 陷入指令 TRAP 必然进入陷入处理程序(异常入口) 改变
Ⅳ 返回 RET 弹出栈顶 PC = 原 CALL 后的那条,正是主程序原本要执行的下一条 不变

为什么 IV 返回算"不改变":

CALL+RET 是配对操作。把 CALL 之前的"主程序"看成一条单线程的指令流:

时刻 主程序视角的"应该执行的下一条"
进入 CALL 之前 主程序里 CALL 之后那条指令
进入子程序、执行 RET 时 同上(栈中保存的就是这个地址)
RET 执行后 PC 指向"主程序里 CALL 之后那条" ✓

也就是说,从主程序的视角,CALL+子程序+RET 这一整段执行下来,主程序的执行序列没有被打断——RET 恰好把控制权送回了"主程序原本要执行的下一条"。这是 IV 区别于 II、III 的关键:II 把控制权送往别处(子程序),III 把控制权送往陷入处理程序,只有 IV 把控制权"还原"。

对比 I 和 IV 的"不改变"含义:

  • I 条件转移可能不改变:分两种情况,条件不满足时不跳转
  • IV 返回总是不改变主程序流:从主程序视角看是恢复,不是打断

题目用"可能不改变"涵盖这两种情况:I 是"有时不改变",IV 是"从主程序视角不改变"——两者都符合"程序原本下一条指令地址未被打断"。

最终答案是 B(Ⅰ、Ⅳ)。

易错点:

  • 这道题的"程序下一条指令的地址"指的是程序逻辑流上的下一条,不是 RET 这条单独指令的 PC+1
  • 过程调用 II 是无条件的,不要因"看得见跳到哪里"而以为可以"不跳"
  • 陷入指令 III 与中断处理同质,必然进入异常处理程序,没有"不进入"的分支
进入练习

第 18 题

计算机组成原理
2 分

某计算机按字节编址,数据 Cache 共有 1024 行,采用 4 路组相联映射,主存块大小为 32B,若访问主存地址为 1028 的 4 字节数据,则该数据所在主存块对应的组号为( )。

A. 4
B. 16
C. 32
D. 64

查看答案与解析收起答案与解析

参考答案:C

地址划分通用流程(组相联映射):

bits-layout 复制代码
width: 32
fields:
  [31, 13] Tag
  [12, 5]  组号
  [4, 0]   块内偏移

上图按本题参数(5 位块内偏移 + 8 位组号 + 19 位 Tag)画出,与标准 32 位地址布局一致。

第一步:求各字段位数

参数 计算 位数
块大小 = 32 B log⁡232=5\log_2 32 = 5 块内偏移占 5 位
总行数 / 路数 = 1024 / 4 = 256 组 log⁡2256=8\log_2 256 = 8 组号占 8 位
剩余 — Tag

第二步:把字节地址 1028 拆成上面三段

地址 1028 的二进制:

1028=1024+4=210+22=0000 0000 0000 0000 0000 0100 0000 0100B1028 = 1024 + 4 = 2^{10} + 2^2 = \texttt{0000\,0000\,0000\,0000\,0000\,0100\,0000\,0100B}

按 [Tag | 组号 | 块内偏移] 切片(自高位到低位 = 19 + 8 + 5 位):

字段 比特位 值(十进制)
块内偏移(低 5 位) 0 0100 4
组号(中间 8 位) 0010 0000 32
Tag(高 19 位) 全 0 0

第三步(验证):用除法核对

步骤 计算 结果
块号 1028÷32=321028 \div 32 = 32(整除) 块号 = 32
组号 32 mod 256=3232 \bmod 256 = 32 组号 = 32
块内偏移 1028 mod 32=41028 \bmod 32 = 4 偏移 = 4

两种方法都得到组号 32。

最终答案是 C(32)。

关键速查:

量 公式
块号 ⌊字节地址/块大小⌋\lfloor \text{字节地址} / \text{块大小} \rfloor
组号 块号  mod \bmod 组数
组数 总行数 // 路数
块内偏移 字节地址  mod \bmod 块大小

避坑:

  • 1024 是总行数,不是组数。组数 = 行数 ÷ 路数 = 256
  • 块号要用字节地址 ÷ 块大小,不能直接用字节地址 mod 组数
  • "4 字节数据"是访问长度,与块大小无关
进入练习

第 19 题

计算机组成原理
2 分

某计算机按字节编址,虚拟地址为 16 位,页大小为 256B,页表项中包含装入位(P)、页框号(PPN)等字段。TLB 采用 4 路组相联映射,共有 16 个页表项,TLB 表项中包含标记(Tag)、有效位(V)等字段。在主存页表与 TLB 表项同步后,若主存页表中页号 22 对应的页表项中 P=0,PPN=2AH,则下列不可能出现在组号为 2 的 TLB 表项中的是( )。

A. Tag=05H,V=1,PPN=1CH
B. Tag=06H,V=1,PPN=2AH
C. Tag=16H,V=0,PPN=2AH
D. Tag=1AH,V=0,PPN=1CH

查看答案与解析收起答案与解析

参考答案:A

第一步:算 TLB 字段宽度

量 计算 值
页内偏移位数 log⁡2256\log_2 256 8 位
页号位数 16 − 8 8 位
TLB 组数 16 项 / 4 路 4 组
组号位数 log⁡24\log_2 4 2 位
Tag 位数 页号位数 − 组号位数 = 8 − 2 6 位

第二步:算页号 22 在 TLB 中的位置

把 8 位页号拆成 [Tag (6 位) | 组号 (2 位)]:

22=0001 0110B22 = \texttt{0001\,0110B}

字段 比特 值
组号(低 2 位) 10 2
Tag(高 6 位) 000101 0x05

所以页号 22 落在 TLB 组 2,对应的 Tag = 05H。

第三步:"页表与 TLB 同步" 的含义

题目说主存页表与 TLB 表项已同步。意思是:TLB 中存在的页表项必须和主存页表内容一致——但这里有重要区别:

"同步" 只对有效条目(V=1)做约束。V=0 的条目内容是历史残留 / 任意值,与页表当前状态无关。

所以同步后的约束是:

如果 TLB 组 2 中某项的 Tag = 05H 且 V=1,那么它的 PPN 必须等于主存页表中页号 22 的 PPN——而页号 22 的 P=0(未装入),意味着页表中根本没有有效的 PPN 可"同步"过来,TLB 中该位置如果 V=1 就矛盾了。

第四步:逐项核对组号 2 的 TLB 内容

选项 Tag 对应页号 V 含义 是否可能
A 05H 5×4+25 \times 4 + 2 = 22 1 声称"页号 22 在 TLB 中有效",与"页表里 P=0(未装入)"矛盾 不可能
B 06H 6×4+26 \times 4 + 2 = 26 1 是页号 26 的有效 TLB 项,与页号 22 无关 可能
C 16H 0x16×4+20x16 \times 4 + 2 = 90 0 V=0 无效项,其它字段是残留值,任意都行 可能
D 1AH 0x1A×4+20x1A \times 4 + 2 = 106 0 V=0 无效项,残留值任意 可能

关键判据:

  • 页号 → Tag:Tag = 页号 ÷ 组数 = 页号的高位部分;不同页号的 Tag 通常不同
  • 同 Tag 才指同页:A 的 Tag=05H 唯一对应页号 22;B 的 Tag=06H 对应的是页号 26
  • V=0 时所有其他字段都不受约束

最终答案是 A。

易错抓手:

  1. 不要看到 PPN 一样就判"冲突"——不同虚页映射到同一物理页是常见情况(共享页)
  2. V=0 的 TLB 项中 PPN、Tag 都是垃圾值,不能用"PPN 应等于 X" 反推
  3. 真正的冲突只发生在 V=1 且 Tag 指向已知"未装入"页号的情形
进入练习

第 20 题

计算机组成原理
2 分

在不考虑异常中断处理和访存的额外开销下,下列关于数据通路结构与 CPI 之间的关系正确的为( )。

Ⅰ. 单周期数据通路计算机的 CPI 等于 1

Ⅱ. 多周期数据通路计算机的 CPI 大于 1

Ⅲ. 流水线数据通路计算机的 CPI 等于 1

A. 仅Ⅰ、Ⅱ
B. 仅Ⅰ、Ⅲ
C. 仅Ⅱ、Ⅲ
D. Ⅰ、Ⅱ、Ⅲ

查看答案与解析收起答案与解析

参考答案:D

核心区分——三种数据通路与 CPI 的关系(理想稳态):

数据通路 1 条指令花几个时钟周期 CPI 时钟周期长度
单周期 1 =1= 1 由最慢指令决定(很长)
多周期 多个(每段 1 周期) >1> 1 由最慢的"段"决定(短)
流水线(理想稳态) 多个段,但每周期完成一条 =1= 1 同多周期(短)

逐项验证:

Ⅰ. 单周期 CPI = 1 ✓

定义:一条指令从取指到写回,全部在一个时钟周期内完成(电路足够长,让信号在一个 tick 里跑完所有部件)。

代价:时钟周期 = 最慢指令所需时间(如 LW 要 IF+ID+EX+MEM+WB 全跑),所有指令都按这个长周期算。

CPI 计算:总周期数总指令数=NN=1\frac{\text{总周期数}}{\text{总指令数}} = \frac{N}{N} = 1。

Ⅱ. 多周期 CPI > 1 ✓

定义:一条指令拆成多个段(如 IF / ID / EX / MEM / WB),每段占一个时钟周期,下一条指令必须等当前指令做完所有段后才能发射。

具体值:取决于指令类型——

指令 段数(典型) CPI 贡献
ALU 类(不访存) IF + ID + EX + WB = 4 4
LW(访存读) IF + ID + EX + MEM + WB = 5 5
SW(访存写) IF + ID + EX + MEM = 4 4
分支类 IF + ID + EX = 3 3

平均 CPI = 各类指令的加权平均,必然 ≥3\geq 3,不可能等于 1。

Ⅲ. 流水线 CPI = 1(理想稳态) ✓

定义:把多周期的多个段重叠执行——指令 i 在 EX 段时,指令 i+1 已在 ID 段,指令 i+2 在 IF 段……每个时钟周期都有一条指令完成(从 WB 段流出)。

pipeline-timing 复制代码
stages: IF, ID, EX, MEM, WB
instructions:
  I1 @ 1: IF ID EX MEM WB
  I2 @ 2: IF ID EX MEM WB
  I3 @ 3: IF ID EX MEM WB
  I4 @ 4: IF ID EX MEM WB
  I5 @ 5: IF ID EX MEM WB

5 条指令、9 个时钟周期完成;当 N 足够大时,CPI = N/(N+4)→1N / (N + 4) \to 1。题目"不考虑访存额外开销和异常中断"正是为了把这个理想稳态条件成立。

全部 Ⅰ、Ⅱ、Ⅲ 都正确。

最终答案是 D(Ⅰ、Ⅱ、Ⅲ)。

速记口诀:

类型 一句话特征 CPI
单周期 一条指令一个长周期 =1= 1
多周期 一条指令多个短周期 >1> 1
流水线 多条指令重叠跑短周期 →1\to 1(理想)

注意"CPI = 1"不一定意味着性能好——单周期的时钟周期被拖得很慢,绝对速度反而最差;流水线的时钟周期短得多,相同 CPI 下性能最高。

进入练习

第 21 题

计算机组成原理
2 分

在 I/O 子系统中,驱动程序和中断服务程序直接控制外设与主机之间的输入/输出操作,这一过程需要使用一些特权指令。下列指令中,不属于特权指令的是( )。

A. I/O 指令
B. 关中断指令
C. 中断返回指令
D. 系统调用指令

查看答案与解析收起答案与解析

参考答案:D

特权指令 vs 非特权指令 的判据:

一条指令是否能威胁系统安全 / 让操作系统失去控制?能 → 必须是特权指令,只能在内核态执行。

特权指令的几大类(必须内核态):

类别 例子 特权原因
直接操作硬件 I/O 指令(IN / OUT)、读写控制寄存器 绕过内核可破坏共享设备
改变 CPU 状态 切换 CPU 模式、装载页表、清空 TLB 改了状态可特权升级
中断管理 开 / 关中断、设置中断向量、中断返回(IRET) 关中断或假返回可绕过抢占调度
进程 / 内存隔离 修改段表 / 页表寄存器、加载 PSW 越界访问其他进程
停机指令 HLT 用户态停机会瘫痪整机

非特权指令(用户态可用):

  • 算术 / 逻辑运算(ADD、SUB、AND)
  • 数据传送(MOV、LOAD、STORE,通过虚地址)
  • 控制流(JMP、CALL、RET、条件跳转)
  • 系统调用指令(SYSCALL / TRAP / INT)

为什么系统调用指令不是特权指令:

系统调用指令的目的恰恰是"让用户态程序主动陷入内核"——用户写 read() 库函数最终编译成 SYSCALL 指令,这条指令本身必须能在用户态执行,否则用户根本进不了内核,整个系统调用机制就崩了。

它与特权指令的关系:

复制代码
用户态 ──── SYSCALL(非特权,从用户态执行)─────▶ 进入内核态
                                                     │
                                                     ├─ I/O 指令(特权)
                                                     ├─ 关中断(特权)
                                                     ├─ 中断返回(特权)
                                                     │
                                                     └─ IRET ───▶ 返回用户态

陷入内核之后,CPU 自动切换到内核态,此时才能执行特权指令。SYSCALL 是"陷入"动作,不是"特权"操作——区别在于"它是允许从用户态发起的、安全的入口"。

逐项判断:

选项 类别 是否特权
A. I/O 指令 直接读写设备 ✓ 特权
B. 关中断指令 中断管理 ✓ 特权
C. 中断返回指令 中断管理(含模式切换) ✓ 特权
D. 系统调用指令 用户主动陷入入口 ✗ 非特权

最终答案是 D。

易混淆抓手:

"用户能用的接口(系统调用)"和"内核才能用的指令(特权指令)"是接力关系:用户态发起 SYSCALL(非特权)→ 陷入内核 → 内核里执行特权指令 → IRET 返回用户态。SYSCALL 是入口、不是特权操作。

进入练习

第 22 题

计算机组成原理
2 分

中断控制 I/O 方式下,实现 I/O 需要硬件和软件协同完成,中断响应和处理过程中所包含的下列工作中,必须由硬件完成的是( )。

A. 开中断
B. 中断
C. 保存断点
D. 保存通用寄存器

查看答案与解析收起答案与解析

参考答案:C

中断响应和处理过程的硬软件分工:

步骤 谁做 何时
① 关中断 硬件(隐指令) 响应瞬间
② 保存断点(PC、PSW) 硬件(隐指令) 关中断之后
③ 形成入口送 PC 硬件(隐指令) 保断点之后
④ 保护现场(保存通用寄存器) 软件(ISR 第一段) 进入 ISR
⑤ 中断处理 软件(ISR 主体) 现场保护后
⑥ 恢复现场(恢复通用寄存器) 软件(ISR 末尾) 处理结束后
⑦ 开中断 软件(指令) 多重中断 ISR 中段开 / 单级中断 IRET 前隐式开
⑧ 中断返回(IRET) 硬件 + 软件配合 ISR 末尾

为什么"保存断点"必须硬件做:

断点 = PC + PSW(被打断时下一条指令的地址 + 处理器状态)。保存断点必须发生在响应中断的瞬间——如果不立刻保住:

  1. 隐指令第三步要把 PC 改写为中断入口地址,原 PC 一旦覆盖就再也找不回来(无法返回原程序)
  2. 关中断动作改变了 PSW 的中断使能位,原 PSW 需保护
  3. 软件根本来不及介入——保存动作必须在硬件级别完成,没有任何机会让软件先跑一段代码

而保存通用寄存器则不一样——

  • 通用寄存器在进入 ISR 后才保护,因为 ISR 头几行还没"用脏"通用寄存器
  • 哪些寄存器要保护、保护到哪个内存区,由 OS / 编译器约定的调用约定(ABI)决定,不是硬件能决定的
  • 只需软件在合适时机 push 即可

逐项审计:

选项 操作 必须由谁做
A 开中断 软件(具体指令如 STI / IRET 隐式开)
B "中断" 题面语义不清,不是具体操作
C 保存断点 硬件(隐指令) ✓
D 保存通用寄存器 软件(ISR 第一段)

最终答案是 C(保存断点)。

关联题对照(中断隐指令系列):

  • 2010-21:单级中断 ISR 内执行顺序(I → V → VI → II → VII)
  • 2011-21:多重中断屏蔽字设置
  • 2012-22:隐指令包含哪些步骤(关中断 + 保断点 + 送入口)
  • 2017-22:多重中断中 CPU 是否全程关中断
  • 2018-22:中断响应的前提条件(IF = 1)
  • 2026-22:哪一步必须由硬件做

把这六道连起来看,"中断响应 + 多重中断 + 屏蔽字 + 硬软件分工"基本就闭环了。

进入练习

第 23 题

操作系统
2 分

下列操作中,在内核模式执行的是( )。

A. 编译程序
B. 链接程序
C. 装入程序
D. 命令解释程序

查看答案与解析收起答案与解析

参考答案:C

判定原则:一个程序是不是在内核态运行,看它是否必须使用特权指令——访问硬件、修改页表、分配物理内存、屏蔽中断等动作只能在内核态做;纯粹的数据加工(读/写文件、字符串处理、算术运算)通过系统调用走一遭就够了,本体仍在用户态。

逐项分析:

选项 程序 主要工作 需要特权指令吗 归属
A 编译程序 词法/语法分析、生成目标代码 否(只是文件读写) 用户态
B 链接程序 合并目标文件、解析符号、重定位 否(只是文件读写) 用户态
C 装入程序 为新进程分配物理内存、建立页表、把代码段/数据段映射进虚地址空间、初始化 PCB 是(修改页表、分配物理页框) 内核态
D 命令解释程序 解析命令、调用 fork/exec 启动子进程 否(自己只是个普通进程) 用户态

A、B、D 三者都是"用户态应用程序",它们如果需要内核服务(比如读文件、创建进程),就发起系统调用进内核走一趟,回来仍在用户态。只有装入程序因为要直接操作页表、分配物理内存、设置进程上下文,本质上是 OS 内核的一部分,必须在内核态执行。

最终答案是 C。

编者注(解题技巧):判定"是否在内核态"只看一句话——这个程序要不要直接动页表/物理内存/特权指令。要动 → 内核态;只读写文件就能完事 → 用户态。"听起来重要"或"和 OS 相关"都不算证据。

进入练习

第 24 题

操作系统
2 分

在支持虚拟存储器系统下的指令执行过程中,正确的是( )。

A. 地址转换由操作系统完成
B. 页表项的内容由编译器确定
C. 缺页中断由硬件直接处理
D. 异常由操作系统处理

查看答案与解析收起答案与解析

参考答案:D

判定思路:抓住"虚存系统中谁做哪一步"的标准分工:

阶段 谁做 做什么
程序编译 编译器(用户态工具) 生成相对/虚地址;不触碰页表项
程序装入 OS(内核态) 分配物理页框、建立页表、填写页表项
指令执行时的地址转换 硬件 MMU + TLB 虚地址 → 物理地址
缺页 / 越权 / 非法指令 硬件检测 → OS 处理 硬件产生异常信号;OS 异常处理程序响应

逐项核对:

  • A 错:地址转换由 MMU 硬件完成,不是 OS。
  • B 错:页表项由 OS 填写,不是编译器。
  • C 错:缺页只是硬件触发异常,真正"处理"——找空闲页框、触发置换、读盘、改页表——是 OS 软件做的。
  • D 对:所有异常(缺页、越权、非法指令、除零等)都遵循"硬件检测 → 触发异常 → OS 处理"的模式。

一句话归纳:简单/高频的操作交硬件做(MMU 翻译地址),复杂/罕见的处理交 OS 做(异常、缺页、I/O)——这是"软硬件协同"的设计哲学。

最终答案是 D。

进入练习

第 25 题

操作系统
2 分

下列关于线程的描述中,正确的是( )。

A. 内核级线程和用户级线程都由操作系统创建
B. 多个内核级线程可以映射到一个用户级线程
C. 同一个进程下的多个内核级线程共享进程栈
D. 同一个进程下的多个线程共享进程堆

查看答案与解析收起答案与解析

参考答案:D

逐项判断:

选项 关键论断 对错 理由
A 两类线程都由 OS 创建 ✗ 用户级线程由用户线程库创建,OS 不可见
B 多个 KLT → 一个 ULT ✗ 多对一模型方向反了,应是多个 ULT → 一个 KLT
C 同进程的 KLT 共享进程栈 ✗ 线程私有栈;共享的是堆、全局区、代码段
D 同进程的多个线程共享进程堆 ✓ 线程的核心特征——共享地址空间内的堆和全局数据

核心知识点:

  • 谁创建:用户级线程 → 用户线程库;内核级线程 → 操作系统
  • 谁私有:栈、寄存器、PC、TCB
  • 谁共享:堆、全局变量、代码段、打开文件、地址空间

线程的"轻",正是因为共享了进程的大部分资源(堆、代码、文件),切换时只需保存/恢复少量私有数据(寄存器和栈指针)。

最终答案是 D。

进入练习

第 26 题

操作系统
2 分

系统中有 8 个进程,执行下图的操作,资源 S 的初始值为 5。若此时 S 的值为 -2,其中 m 表示执行到访问资源的进程个数,n 表示阻塞的进程个数,则 m 和 n 的值分别是( )。

C 复制代码
操作
wait(S)
访问资源
signal(S)

A. 5, 2
B. 5, 1
C. 6, 2
D. 7, 1

查看答案与解析收起答案与解析

参考答案:A

信号量语义回顾(这两条是这题的全部):

  1. wait(S):先 S--;若 S < 0,调用进程阻塞进入 S 的等待队列;若 S ≥ 0,进程继续执行进入临界区
  2. 信号量为负时,∣S∣|S| 恰好等于阻塞队列上的进程数——这是判断阻塞数最快的捷径

逐步推导:

状态 S 值 累计 wait 次数 已访问 阻塞 还未来
初始 +5 0 0 0 8
第 1 个 wait +4 1 1 0 7
第 2 个 wait +3 2 2 0 6
第 3 个 wait +2 3 3 0 5
第 4 个 wait +1 4 4 0 4
第 5 个 wait 0 5 5 0 3
第 6 个 wait −1 6 5 1 2
第 7 个 wait −2 7 5 2 1

到 S = −2 时:

  • mm = 当前在临界区访问资源的进程数 = 5(这正是信号量初值——临界区"容量"被 5 卡死,无法多于 5)
  • nn = 阻塞队列长度 = ∣S∣|S| = 2

两条速记结论:

  • SS 为正时:表示剩余可用资源数
  • SS 为负时:∣S∣|S| 表示阻塞队列长度;同时正在访问资源的进程数已达初值上限

最终答案是 A(5, 2)。

进入练习

第 27 题

操作系统
2 分

假设进程 P 的读、写进程集合分别是 R(P) 和 W(P),进程 Q 的读、写进程集合分别为 R(Q) 和 W(Q),则进程 P 和 Q 并发执行中,不会发生错误的并发执行充要条件是( )。

Ⅰ. R(Q)∩W(P)=∅
Ⅱ. R(P)∩R(Q)=∅
Ⅲ. W(P)∩W(Q)=∅
Ⅳ. R(P)∩W(Q)=∅

A. Ⅰ、Ⅱ
B. Ⅰ、Ⅱ、Ⅲ
C. Ⅰ、Ⅲ、Ⅳ
D. Ⅱ、Ⅲ

查看答案与解析收起答案与解析

参考答案:C

Bernstein 条件:进程 P 与 Q 可安全并发的充要条件是下面三组集合两两不相交:

冲突类型 不相交条件 物理含义
写后读(P 写 → Q 读同一变量) W(P)∩R(Q)=∅W(P)\cap R(Q)=\emptyset 防止 Q 读到 P 写一半的脏数据
读后写(Q 写 → P 读同一变量) R(P)∩W(Q)=∅R(P)\cap W(Q)=\emptyset 防止 P 读到的值被 Q 偷换
写写冲突(双方都写同一变量) W(P)∩W(Q)=∅W(P)\cap W(Q)=\emptyset 防止两方写入互相覆盖、最终值不确定

唯一不需要的是"读读不相交"——两个进程同时读同一变量,谁也不会改它,互不影响。

对照题目四个选项:

  • Ⅰ. R(Q)∩W(P)=∅R(Q)\cap W(P)=\emptyset → 等价于 W(P)∩R(Q)=∅W(P)\cap R(Q)=\emptyset,写后读冲突 ✓
  • Ⅱ. R(P)∩R(Q)=∅R(P)\cap R(Q)=\emptyset → "读读不相交",不必要 ✗
  • Ⅲ. W(P)∩W(Q)=∅W(P)\cap W(Q)=\emptyset → 写写冲突 ✓
  • Ⅳ. R(P)∩W(Q)=∅R(P)\cap W(Q)=\emptyset → 读后写冲突 ✓

需要同时满足的是 Ⅰ、Ⅲ、Ⅳ。

记忆口诀:"写写、读写、写读三冲突,读读相安无事"——三个含 W 的方向都要不相交,只剩 RR 不用管。

最终答案是 C。

进入练习

第 28 题

操作系统
2 分

若 64 位的系统采用三级虚拟分页存储管理方式,其结构如下表所示,第三级页表所占用的页框数是( )。

复制代码
| 补充位(25) | 一级页表(9) | 二级页表(9) | 三级页表(9) | 页内偏移(12) |

A. 1
B. 256
C. 256K
D. 256M

查看答案与解析收起答案与解析

参考答案:C

判断思路:分两步——① 一个三级页表占多少页框;② 总共有多少个三级页表。

步骤 1:一个三级页表的大小

项 值
三级页表项数 29=5122^9 = 512
每项大小(64 位系统典型 PTE) 8 字节
一个三级页表大小 512×8=4096 B=4 KB512 \times 8 = 4096\text{ B} = 4\text{ KB}
页大小(由页内偏移 12 位决定) 212=4 KB2^{12} = 4\text{ KB}
一个三级页表占用页框数 4KB/4KB=14\text{KB}/4\text{KB} = \mathbf{1} 个页框

题目给的 9-9-9-12 切分配 64 位 PTE,让"一个页表正好等于一个页框"——这是多级页表的标准设计,让每级页表自己也能像普通页一样换入换出。

步骤 2:三级页表的总个数

逐级展开:

级 项数 每级"分支因子"
一级页表 29=5122^9 = 512 每个一级条目指向 1 个二级页表
全部二级页表的总条目 29×29=2182^9 \times 2^9 = 2^{18} 每个二级条目指向 1 个三级页表
三级页表总数 218=262144=256K2^{18} = 262144 = 256\text{K} —

步骤 3:合并

三级页表占用页框数=256K(个三级页表)×1(页框/个)=256K\text{三级页表占用页框数} = 256\text{K(个三级页表)} \times 1\text{(页框/个)} = \mathbf{256\text{K}}

为什么不是 256M:虚地址 64 位中只有 39 位(9+9+9+129+9+9+12)被有效使用——上面 25 位是"补充位",不参与任何索引,所以总寻址空间是 2392^{39} 而非 2642^{64}。从这 2392^{39} 字节里,按 4KB 页划分能得到 2272^{27} = 128M 个页框(这是数据页),但承载这些数据页所需的三级页表个数是 2182^{18} = 256K,差了一个数量级,因为每个三级页表覆盖了 29×4KB=2MB2^9 \times 4\text{KB} = 2\text{MB} 的虚拟地址空间。

最终答案是 C(256K)。

进入练习

第 29 题

操作系统
2 分

下列方法中能够有效降低系统平均访存时间的是( )。

Ⅰ. TLB
Ⅱ. 多级页表
Ⅲ. 工作集概念
Ⅳ. 页表缓冲队列

A. Ⅰ、Ⅲ
B. Ⅱ、Ⅲ
C. Ⅰ、Ⅲ、Ⅳ
D. Ⅰ、Ⅱ、Ⅳ

查看答案与解析收起答案与解析

参考答案:C

判定原则:把每种方法对"平均访存时间公式"的贡献位置看清楚。

简化模型:

T访存‾≈T地址转换+(1−p缺页)⋅T内存+p缺页⋅T缺页处理\overline{T_{\text{访存}}} \approx T_{\text{地址转换}} + (1 - p_{\text{缺页}}) \cdot T_{\text{内存}} + p_{\text{缺页}} \cdot T_{\text{缺页处理}}

降低任一项就能降低整体——重点是这两项:地址转换时间、缺页率/缺页处理代价。

逐项分析:

项 机制 影响公式哪一项 是否降低访存时间
Ⅰ. TLB 缓存最近访问的页表项 命中时 T地址转换T_{\text{地址转换}} 从一次完整查表降到 1 个 cache 周期 ✓ 显著降低
Ⅱ. 多级页表 把大页表拆成树状多层 把 T地址转换T_{\text{地址转换}} 从 1 次查表变成 kk 次查表 ✗ 反而升高(节省的是空间不是时间)
Ⅲ. 工作集 只保留进程"近期常用"页面在内存 p缺页p_{\text{缺页}} 大幅下降 ✓ 降低(缺页处理是几毫秒级,省一次差好几个数量级)
Ⅳ. 页缓冲队列 被换出的页先入空闲队列,未被覆盖前可零成本取回 即便发生缺页,T缺页处理T_{\text{缺页处理}} 也可能不需走磁盘 ✓ 降低

关键辨析:

  • TLB 是硬件 cache,命中率通常 95%+,对访存时间是数量级的改善
  • 多级页表是空间-时间权衡:用更多次访存换更省的页表内存;想抵消它带来的延迟开销正是 TLB 的作用
  • 工作集理论的实际收益体现在缺页率上——缺页一次几毫秒,普通访存几纳秒,省一次缺页相当于省百万次普通访存
  • 页缓冲队列(如 Linux 的 buddy + LRU 页面回收链)是 OS 层的"软 cache",缺页时先查它再考虑读盘

只有 Ⅰ、Ⅲ、Ⅳ 真正降低访存时间;Ⅱ 是节省空间、反而增加单次访存时延。

最终答案是 C(Ⅰ、Ⅲ、Ⅳ)。

进入练习

第 30 题

操作系统
2 分

进程 P1 和 P2 共享一个文件 R,该文件的页表项分别是 R1 和 R2,其在 2 个进程中的虚拟地址分别是 W1 和 W2,则下列说法中正确的是( )。

A. 页表项 R1 和 R2 的内容完全不同
B. W1 和 W2 映射的物理地址相同
C. 进程 P1 对 W1 的修改不会影响 P2 对 W2 的访问
D. W1 和 W2 虚拟地址相同

查看答案与解析收起答案与解析

参考答案:B

核心机制:进程间共享文件(典型实现是 mmap MAP_SHARED 或 SysV shmget)的本质——

让两个进程的页表项指向同一个物理页框,从而实现"两边看到的字节内容时刻一致"。

对照四个维度:

比较项 P1 视角 P2 视角 是否相等
虚拟地址(W1 / W2) 各自地址空间内分配 各自地址空间内分配 不一定相等
页表项的物理页框号 指向共享物理页 指向同一共享物理页 相等
页表项的保护/修改位 取决于该进程权限 取决于该进程权限 可能不同
物理地址 W1→W1 \to 同一物理页 W2→W2 \to 同一物理页 相等

逐项核对:

  • A 错:物理页框号必然相同,"完全不同"是错的;只有部分标志位可能不同。
  • B 对:共享的本质就是物理地址相同——这是"共享"二字的工作机理。
  • C 错:共享映射下一方写、另一方立即可见,这正是它和"写时复制"的关键区别。
  • D 错:虚地址在各自独立的地址空间,没必要相同。

速记:共享文件 ⇔ 物理地址相同——其余四项(虚地址、PTE 内容、修改可见性)都不绑定。

最终答案是 B。

进入练习

第 31 题

操作系统
2 分

下列关于驱动程序的描述中,错误的是( )。

A. 驱动程序是硬件与操作系统之间的接口程序
B. 驱动程序需根据硬件特性定制开发
C. 驱动程序需要设置统一的接口
D. 字符设备、块设备都是同一种 IO 方式

查看答案与解析收起答案与解析

参考答案:D

题面是反向措辞:"错误的是( )"——找的是错的那个。

分析四个论断:

选项 内容 对错 理由
A 驱动是硬件与 OS 的接口程序 对 教材定义;是连接 OS 抽象层和具体硬件寄存器的桥梁
B 驱动需根据硬件特性定制开发 对 不同硬件寄存器布局、中断号、命令字都不同,必须一对一编写
C 驱动需要设置统一的接口 对 向上必须提供统一的 read/write/ioctl 等接口,让 OS 屏蔽设备差异
D 字符设备、块设备都是同一种 I/O 方式 错 二者是 OS 划分的不同设备类,I/O 方式根本不同

字符设备 vs 块设备 的本质区别:

维度 字符设备 块设备
访问粒度 按字节流式访问 按块(如 512 B / 4 KB)随机访问
缓冲方式 通常无缓冲或简单缓冲 有专门的块缓存(buffer cache / page cache)
是否可寻址 不可(流式,只能顺序读写) 可(任意 LBA 跳转)
典型设备 键盘、鼠标、串口、终端 磁盘、SSD、光盘、U 盘
系统调用语义 一次返回任意字节数 一次必读/写一整块

正因为这两类设备的访问模式完全不同,OS 在 I/O 子系统里给它们设了两条独立的处理路径(Linux 里就是 chrdev 和 blkdev 两套子系统)——绝不是"同一种 I/O 方式"。D 是错的。

最终答案(错误的描述)是 D。

进入练习

第 32 题

操作系统
2 分

下列操作中,鼠标中断处理程序完成的是( )。

A. 解析鼠标的输入指令含义
B. 将鼠标数据同步到用户应用程序缓冲区
C. 将数据从输入设备传输到数据寄存器
D. 将数据从数据寄存器传输到内核缓冲区

查看答案与解析收起答案与解析

参考答案:D

判定思路:把"鼠标按了一下"到"应用程序拿到事件"这个全链路按层次拆开,看每一层各做什么——中断处理程序只负责其中一段。

完整数据流(从下到上):

阶段 谁做 做什么
① 物理层 鼠标硬件 + 设备控制器 检测按键/移动;把数据放入控制器的数据寄存器;向 CPU 发中断信号
② 中断处理 中断处理程序(OS 内核) 从数据寄存器读取数据 → 写入内核缓冲区;ack 中断
③ 设备无关层 OS I/O 软件 把字节级数据组装成"事件"(移动/单击/双击)、放入对应进程等待队列
④ 用户层 应用程序通过 read() 系统调用 触发 copy_to_user,把内核缓冲区的数据复制到应用缓冲区

中断处理程序的关键设计原则是**"快进快出"**——只做必须立即做的最小工作(搬数据离开寄存器、确认中断),其余复杂处理(语义解析、唤醒进程、与用户交互)都交给上层。

逐项核对:

选项 描述 实际由谁做
A 解析鼠标输入指令含义(是单击还是双击) 上层设备无关 I/O 软件或图形系统
B 数据同步到用户缓冲区 用户进程发起的 read() 系统调用(copy_to_user)
C 数据从输入设备到数据寄存器 设备控制器(硬件)
D 数据从数据寄存器到内核缓冲区 中断处理程序 ✓

速记:中断处理 = 把数据从硬件寄存器抢救到内核缓冲区,前后两端都不归它管:之前是硬件、之后是用户的事。

最终答案是 D。

进入练习

第 33 题

计算机网络
2 分

下列关于分层网络体系结构的叙述中,错误的是( )。

A. 每层都有明确的功能边界
B. 层次越多效率越高
C. 有利于各层技术独立演化
D. 上层无需关心下层的具体实现细节

查看答案与解析收起答案与解析

参考答案:B

分层网络体系结构的设计动机:把复杂的通信问题切成若干独立模块,每层只解决一个子问题,上下层之间通过定义良好的**接口(服务原语)**交互。这一设计带来三个直接好处:

  1. 模块化:每层有明确的功能边界(→ A 对)
  2. 独立演化:下层换实现,只要接口不变,上层无感(→ C、D 对)
  3. 简化设计与教学:每层只关心自己的问题,便于工程分工与学术研究

逐项核对:

  • A 对:每层职责清晰是分层的前提。OSI 七层、TCP/IP 四层都是这一原则的体现。
  • B 错:层次越多 ≠ 效率越高。每多一层就多一份封装 / 解封装开销、多一份头部字节、多一次跨层调用。OSI 七层比 TCP/IP 四层"完整"但实际部署中 TCP/IP 胜出,正是因为分层是工程权衡——既要够分(解耦清楚),又不能太多(避免冗余)。
  • C 对:分层最大的实际收益。例:以太网物理层从 10 Mbps 双绞线演化到 100 Gbps 光纤,IP 层完全不受影响。
  • D 对:上层通过下层暴露的接口调用服务,对下层的具体实现机制(用什么介质、用什么帧格式)保持透明——这就是"协议栈"和"封装"的核心思想。

题问"错误的是",唯一错误项是 B。

最终答案是 B。

编者注(生僻术语):"功能边界"在网络分层语境里指每层负责的职责范围——物理层只管 0/1 信号、链路层只管点对点成帧、网络层只管端到端路由。"边界明确"= 每层不越界做别人的事,也是分层架构能成立的前提。

进入练习

第 34 题

计算机网络
2 分

若在带宽 200 kHz,信噪比 S/N=1023 的信道上,发送一个长度为 1500 B 的分组,则发送该分组的传输时延至少是( )。

A. 1 ms
B. 2 ms
C. 3 ms
D. 6 ms

查看答案与解析收起答案与解析

参考答案:D

第一步:用香农定理算信道极限容量

C=Blog⁡2(1+S/N)=200×103×log⁡2(1+1023)=200×103×10=2×106 bpsC = B \log_2(1 + S/N) = 200 \times 10^3 \times \log_2(1 + 1023) = 200 \times 10^3 \times 10 = 2 \times 10^6 \text{ bps}

关键中间值 log⁡2(1024)=10\log_2(1024) = 10(这是题面给 1023 的明显意图——凑成 2102^{10} 整数,方便心算)。

第二步:把分组长度换算成比特

L=1500 B×8=12000 bitL = 1500 \text{ B} \times 8 = 12000 \text{ bit}

第三步:传输时延 = 长度 / 信道速率

T=LC=120002×106=6×10−3 s=6 msT = \frac{L}{C} = \frac{12000}{2 \times 10^6} = 6 \times 10^{-3} \text{ s} = 6 \text{ ms}

为什么是"至少"? 香农定理给的是信道容量上限——实际链路速率不可能超过 C,所以真实传输时延不可能短于 L/CL/C。题面问"至少",等价于问"按理论极限的最优速率,最快多久发完",答案就是 6 ms。

最终答案是 D(6 ms)。

编者注(生僻术语):题里 S/N=1023 是数比形式(线性比值)。考研题里如果遇到 S/N 给 dB 值(如"信噪比 30 dB"),要先换算回数比再代公式:10lg⁡(1+S/N)=30⇒1+S/N=103=1000⇒S/N=99910\lg(1+S/N) = 30 \Rightarrow 1+S/N = 10^3 = 1000 \Rightarrow S/N = 999。

进入练习

第 35 题

计算机网络
2 分

假设采用 CSMA/CA 的 IEEE 802.11 无线局域网,其数据传输速率为 300 Mbps,DIFS = 128 μs,SIFS = 28 μs。忽略除数据帧以外的其他帧的传输时延及信号传播时延,主机 H 发送一个总长度为 1500 B 的数据帧,则从开始发送数据帧至确认接收方收到所需的时间至少为( )。

A. 40 μs
B. 68 μs
C. 168 μs
D. 196 μs

查看答案与解析收起答案与解析

参考答案:B

完整 CSMA/CA 帧交换流程(数据帧 + ACK,无碰撞场景):

阶段 时长 说明
① 发送方监听信道 + 等 DIFS 128 μs 信道必须空闲至少一个 DIFS(128 μs)才能发
② 发送方传数据帧 40 μs 1500 B × 8 / 300 Mbps
③ 接收方等 SIFS 28 μs 数据帧收完之后等一个 SIFS才能回 ACK(保证发送方切换到接收态)
④ 接收方传 ACK 题面忽略 "忽略除数据帧以外的其他帧的传输时延"
⑤ 信号传播时延 题面忽略 "及信号传播时延"

题面起点是"开始发送数据帧"——也就是从阶段 ② 开始。①(DIFS)已经在起点之前结束了。

计算阶段 ② + ③ + ④ + ⑤:

  • 阶段 ②:1500×8 bit300×106 bps=120003×108=4×10−5\dfrac{1500 \times 8 \text{ bit}}{300 \times 10^6 \text{ bps}} = \dfrac{12000}{3 \times 10^8} = 4 \times 10^{-5} s = 40 μs
  • 阶段 ③:SIFS = 28 μs(必经流程,不能省,否则发送方还来不及切到接收态)
  • 阶段 ④:题面规定忽略 → 0
  • 阶段 ⑤:题面规定忽略 → 0

合计 40+28=6840 + 28 = 68 μs。

最终答案是 B(68 μs)。

编者注(生僻术语):DIFS(Distributed Inter-Frame Space)和 SIFS(Short Inter-Frame Space)是 802.11 里两个不同优先级的帧间间隔。SIFS 最短(28 μs,本题数值),用于最高优先级动作(ACK / CTS 等紧接前一帧的回应);DIFS 较长(128 μs,本题数值),用于普通数据帧竞争信道前的等待。SIFS < DIFS 这条不等式保证了 ACK 总是抢在新数据帧之前发出去,不会被打断。

进入练习

第 36 题

计算机网络
2 分

支持 VLAN 划分的以太网交换机,已按端口划分了两个 VLAN。VLAN 划分结果及各端口连接主机的 MAC 地址如下图所示。下列具有不同目的 MAC 地址(DA)和源 MAC 地址(SA)的以太帧 F1~F4 中,H3 会接收到的是( )。

图片待补充

结构(文字版):一台 24 端口以太网交换机(端口排列上下两排:上排 13–24,下排 1–12),由一道虚线竖向把上下两排同时切成左右两块,分出两个 VLAN:

  • VLAN A(左半区):占用上排端口 13–19、下排端口 1–7
    • H1(MAC 00-1A-2B-3C-4D-01)接端口 13
    • H2(MAC 00-1A-2B-3C-4D-02)接端口 2
    • H3(MAC 00-1A-2B-3C-4D-03)接端口 5
  • VLAN B(右半区):占用上排端口 20–24、下排端口 8–12
    • H4(MAC 00-1A-2B-3C-4D-04)接端口 8
    • H5(MAC 00-1A-2B-3C-4D-05)接端口 9
    • H6(MAC 00-1A-2B-3C-4D-06)接端口 12

其余端口(图中未连任何主机)暂不参与本题。

四个候选帧的地址如下:

  • F1:DA = 00-1A-2B-3C-4D-03(H3);SA = 00-1A-2B-3C-4D-01(H1)
  • F2:DA = 00-1A-2B-3C-4D-04(H4);SA = 00-1A-2B-3C-4D-05(H5)
  • F3:DA = FF-FF-FF-FF-FF-FF(广播);SA = 00-1A-2B-3C-4D-02(H2)
  • F4:DA = 00-1A-2B-3C-4D-06(H6);SA = 00-1A-2B-3C-4D-03(H3)

A. 仅 F2、F4
B. 仅 F1、F3
C. 仅 F1、F2
D. 仅 F3、F4

查看答案与解析收起答案与解析

参考答案:B

两条转发规则锁定答案:

  1. VLAN 隔离:跨 VLAN 帧(含单播 + 广播 + 未学习的 MAC 泛洪)一律被交换机丢弃在源 VLAN 内,不会跨 VLAN 转发(要互通须靠三层设备,本题无)。
  2. 不向入端口回转:交换机绝不把帧再从它进来的那个端口送出去,所以主机自己发的帧自己永远收不到(哪怕是广播)。

逐帧判定:

帧 DA SA DA 在哪 VLAN SA 在哪 VLAN H3 是否收?
F1 H3 H1 A A ✅ 同 VLAN 单播给 H3,H3 收
F2 H4 H5 B B ❌ 同 VLAN B 内的转发,与 VLAN A 完全隔离
F3 广播 H2 —(广播) A ✅ 在 VLAN A 内广播,H3 与 H2 同 VLAN,H3 收
F4 H6 H3 B A ❌ 双重原因:① H3 自己发的,不会回到入端口 5;② 跨 VLAN(H3 在 A,H6 在 B),即便不算回转规则也跨 VLAN 出不去

H3 收到的帧 = F1 + F3。

最终答案是 B(仅 F1、F3)。

编者注(生僻术语):"不向入端口回转"是 IEEE 802.1D 透明网桥转发规则的兜底约束——既适用于已学习的 MAC(直接查表转发到对应端口)、也适用于未学习的 MAC 泛洪(向除入端口外所有同 VLAN 端口送)、还适用于广播帧(向同 VLAN 所有端口送但不送回入端口)。三种情形都遵守这条不回转规则。

进入练习

第 37 题

计算机网络
2 分

某网络在 t0 时刻的网络拓扑与 R1 的路由如下图所示。R1 ~ R4 为路由器,基于链路状态路由算法进行路由计算。S0 ~ S3 为路由器 R1 的接口,链路上的数值为链路开销。若在 t1(t1 > t0)时刻,R1 检测到 R1 与 R2 之间的链路断开,则 R1 重新计算路由并进行充分路由聚合后,路由表中路由条目的数量为( )。

network-topology 复制代码
nodes:
  Net1 [cloud] @ (0, 0) | "199.10.20.0/27"
  R2 [router] @ (2, 0) | "R2"
  R3 [router] @ (4, 0) | "R3"
  Net2 [cloud] @ (6, 0) | "199.10.20.32/27"
  Internet [cloud] @ (0, 1.5) | "Internet"
  R1 [router] @ (2, 1.5) | "R1"
  R4 [router] @ (4, 1.5) | "R4"
  Net3 [cloud] @ (6, 1.5) | "199.10.20.64/27"
  Host [server] @ (2, 3)
  Net4 [cloud] @ (4, 3) | "199.10.20.128/25"
edges:
  Net1 -- R2
  R2 -- R3 : "5"
  R3 -- Net2
  Internet -- R1 (S0)
  R1 (S1) -- R2 : "2"
  R1 (S2) -- R3 : "6"
  R1 (S3) -- R4 : "3"
  R3 -- R4 : "4"
  R4 -- Net3
  R1 -- Host
  R4 -- Net4

A. 3
B. 4
C. 5
D. 6

查看答案与解析收起答案与解析

参考答案:C

第一步:算 t1 时刻 R1 到各外部子网的最短路径(Dijkstra)

链路 R1-R2 断后,R1 直连邻居只剩 R3(cost 6)、R4(cost 3)。各目的子网由谁直连:

子网 直连路由器 R1 经过哪条最短路 总开销 下一跳
199.10.20.0/27 R2 R1 → R3 → R2 6 + 5 = 11 R3
199.10.20.32/27 R3 R1 → R3 6 R3
199.10.20.64/27 R4 R1 → R4 3 R4
199.10.20.128/25 R4 R1 → R4 3 R4

验证 R1 → R4 → R3 → R2(3+4+5=12)确实比 R1 → R3 → R2(6+5=11)大,所以走 R3。R1 → R4 → R3(3+4=7)也比 R1 → R3(6)大,所以到 R3 直接走 R1-R3 链路即可。

第二步:CIDR 聚合规则

两条路由能合并必须同时满足:

  1. 下一跳相同(否则合并后无法决定送往哪边)
  2. IP 前缀连续可合并(去掉若干位掩码后能覆盖且只覆盖原来的范围)

逐对检查:

  • .0/27(next R3)+ .32/27(next R3)→ 下一跳同;前缀 199.10.20.0 与 199.10.20.32 在 /26 下都属于 199.10.20.0/26 ✅ → 合并为 199.10.20.0/26 → R3
  • .64/27(next R4)+ .128/25(next R4)→ 下一跳同,但 .64 与 .128 在二进制位上非相邻区间(中间隔着 .96~.127),不能聚合 ❌
  • .0/26(next R3)+ .64/27(next R4)→ 下一跳不同 ❌

第三步:清点 t1 时刻 R1 的路由表

network-topology 复制代码
nodes:
  Net1 [cloud] @ (0, 0) | "199.10.20.0/27"
  R2 [router] @ (2, 0) | "R2"
  R3 [router] @ (4, 0) | "R3"
  Net2 [cloud] @ (6, 0) | "199.10.20.32/27"
  Internet [cloud] @ (0, 1.5) | "Internet"
  R1 [router] @ (2, 1.5) | "R1"
  R4 [router] @ (4, 1.5) | "R4"
  Net3 [cloud] @ (6, 1.5) | "199.10.20.64/27"
  Host [server] @ (2, 3)
  Net4 [cloud] @ (4, 3) | "199.10.20.128/25"
edges:
  Net1 -- R2
  R2 -- R3 : "5"
  R3 -- Net2
  Internet -- R1 (S0)
  R1 (S2) -- R3 : "6"
  R1 (S3) -- R4 : "3"
  R3 -- R4 : "4"
  R4 -- Net3
  R1 -- Host
  R4 -- Net4
highlight:
  edges: R1--R3, R2--R3

上图为 t1 时刻拓扑(已删除断开的 R1-R2 链路)。蓝色高亮的 R1 → R3 → R2 即为 R1 到 199.10.20.0/27 子网的新最短路径。

聚合后 R1 路由表:

序号 目的网络 下一跳
1 199.10.20.0/26 R3(覆盖 .0/27 + .32/27)
2 199.10.20.64/27 R4
3 199.10.20.128/25 R4
4 0.0.0.0/0(默认) Internet
5 R1 直连子网(含 Host) 直连

合计 5 条。

最终答案是 C(5)。

编者注(生僻术语):"充分路由聚合"在 408 题里意思是——所有可以合并的连续前缀都要合(等价于 CIDR Supernetting 用到极致),但绝不能跨下一跳合也绝不能合并不连续前缀。这两条边界考点经常出现在错项里。

进入练习

第 38 题

计算机网络
2 分

下列路由协议中,能将一个自治系统划分为多个区域的内部网关协议是( )。

Ⅰ. OSPF
Ⅱ. RIP
Ⅲ. BGP

A. 仅 Ⅰ
B. 仅 Ⅱ
C. 仅 Ⅰ、Ⅲ
D. 仅 Ⅱ、Ⅲ

查看答案与解析收起答案与解析

参考答案:A

第一步:先按"内部网关协议(IGP)vs 外部网关协议(EGP)"分类:

协议 类型 用途
RIP IGP(距离向量) AS 内部,跳数 ≤ 15 的小型网络
OSPF IGP(链路状态) AS 内部,中大型网络
BGP EGP(路径向量) AS 之间交换路由

III 排除:BGP 不是 IGP,与题面要求"内部网关协议"矛盾。

第二步:在剩下的 IGP(RIP、OSPF)里看谁支持区域划分:

  • OSPF 的 area:把一个 AS 切成多个 area(编号 0、1、2…,area 0 必为骨干)。区域内部跑链路状态算法(每台路由器持有 area 内完整 LSDB),区域之间通过 ABR(Area Border Router)汇总通告。这样做的收益:
    1. 限制 LSA 泛洪范围——LSA 只在本 area 内泛洪,跨 area 由 ABR 摘要重发,控制控制平面开销
    2. 缩小 LSDB 大小——每台路由器只装本 area 的拓扑,节省内存
    3. 加快收敛——一个 area 内拓扑变化不会触发别的 area 重新计算
  • RIP 不支持区域:RIP 只有"自治系统"一个层级,所有路由器同等地相互广播 RIP 报文,不存在 area 概念。RIP 的扩展能力靠"跳数硬上限 15"硬扛,规模一大就崩。

III 排除(已在第一步),II 排除,仅 I 满足。

最终答案是 A(仅 Ⅰ)。

编者注(生僻术语):BGP 自身确实有一种叫 "联邦(confederation)" 的机制把一个大 AS 拆成若干子 AS,看起来"像"分区。但这是 BGP 在 EGP 角色下的内部组织手段,不属于 IGP 的区域划分,且术语是"sub-AS / 联邦"不是"area"。考研题里只要看到"内部网关协议 + 区域划分"就锁定 OSPF。

进入练习

第 39 题

计算机网络
2 分

若将 IP 网络 123.4.4.0/22 划分为规模均衡的 32 个子网,则 IP 地址 123.4.5.11 所在的子网是( )。

A. 123.4.4.0/27
B. 123.4.4.32/27
C. 123.4.5.0/27
D. 123.4.5.32/27

查看答案与解析收起答案与解析

参考答案:C

第一步:算新前缀长度

原网络 /22 划成 32 个均衡子网,借 log⁡232=5\log_2 32 = 5 位作为子网号 → 新前缀 22+5=2722 + 5 = 27,即每个子网都是 /27。

第二步:算每个子网容量

/27 的主机位 = 32−27=532 - 27 = 5 位 → 每个子网 25=322^5 = 32 个 IP 地址(包含网络号和广播)。

第三步:定位 123.4.5.11 所属 /27

把 IP 写成二进制(重点是第三、第四字节,前两字节 123.4 不变):

字段 第三字节(.5) 第四字节(.11)
二进制 0000 0101 0000 1011

/27 网络位 = 前 27 位 = 前两字节 16 位 + 第三字节 8 位 + 第四字节高 3 位。

第四字节 0000 1011 的高 3 位 = 000,对应主机位为 0 1011。

把主机位清零得到子网网络号:

字段 二进制 十进制
第三字节 0000 0101 5
第四字节(仅高 3 位保留) 000 0 0000 0

子网网络号 = 123.4.5.0/27,覆盖范围 .5.0 ~ .5.31,123.4.5.11 落在区间内 ✓。

验证另一种思路(按 /27 步长 32 跳):

  • /27 子网网络号步长 = 32
  • 第四字节边界:.0、.32、.64、.96、.128、.160、.192、.224
  • 11 < 32 → 落在以 .0 起始的子网

第三字节按步长 1 递增(每个 /27 里第四字节走完一轮 32 个就推进第三字节,但第三字节本身完整保留),123.4.5.x 所在那一段以 .5.0 / .5.32 / .5.64 / … 划开,11 在 [0, 32) 内 → 123.4.5.0/27。

最终答案是 C(123.4.5.0/27)。

编者注(生僻术语):"规模均衡"在子网划分题里特指"所有子网容量相同"——所以从 /22 划 32 个等容量子网就是统一借 5 位、统一变 /27。如果是"规模不等"则要走 VLSM(Variable Length Subnet Mask),按需分配不同长度的前缀。

进入练习

第 40 题

计算机网络
2 分

下列叙述中不属于 cookie 的技术典型用途的是( )。

A. 用户跟踪
B. 个性化推荐
C. 构建虚拟购物车
D. 缩短 Web 对象的响应时间

查看答案与解析收起答案与解析

参考答案:D

Cookie 是什么:RFC 6265 定义的 HTTP 状态保持机制——HTTP 协议本身无状态,每个请求互相独立;服务器通过 Set-Cookie 响应头把一段小数据(≤ 4 KB)写入客户端,浏览器之后每次访问同域名都自动在 Cookie 请求头里把它带回服务器,从而让服务器识别"是谁来了"。

Cookie 的典型用途(围绕"识别 + 持久化少量状态"展开):

用途 实现思路
会话管理(登录态) cookie 存 session ID,服务器拿 ID 查 session 库
用户跟踪 cookie 存唯一用户 ID,记录"老访客 / 新访客"、访问轨迹
个性化推荐 cookie 标识用户身份 → 关联其浏览偏好 → 算法基于此推荐
购物车 cookie 存商品 ID 列表 / 数量,跨页面、跨会话保持
偏好设置 cookie 存语言、主题、字号等用户选择

为什么 D 错:缩短响应时间是 Web 缓存(浏览器缓存、Cache-Control / ETag、代理缓存、CDN)的功能,不是 cookie 的功能。两个机制完全独立:

  • 缓存 解决的是"同一份资源不必重复传输"——通过 Cache-Control、ETag、Last-Modified 这些头部协商。
  • Cookie 解决的是"如何识别用户身份"——它甚至会增加每次请求的开销(每个请求都要带回服务端)。

把这两者混为一谈是 D 项的核心陷阱。

最终答案是 D(缩短 Web 对象的响应时间)。

编者注(生僻术语):"Web 对象"指 HTTP 响应里的资源实体(HTML 页、图片、CSS、JS 文件等)。缩短 Web 对象响应时间的标准手段:① 浏览器本地缓存(Cache-Control: max-age);② 条件请求(If-None-Match + 304 Not Modified);③ 代理缓存 / CDN;④ HTTP/2 多路复用;⑤ 持久连接(Connection: keep-alive)。这些都跟 cookie 无关。

进入练习

综合应用题

7 题 · 共 70 分

第 41 题

数据结构
13 分

(本题满分 13 分)

假定二叉搜索树使用二叉链表存储,存储结构如下:

c 复制代码
typedef struct BSTNode {
    int data;
    struct BSTNode *left, *right;
} BSTNode;

给一棵二叉搜索树 T 和整数 K,查找树中关键字与 K 之差的绝对值最小的所有结点,并输出该绝对值与结点中的关键字。

(1) 给出算法的基本设计思想。(4 分)

(2) 使用 C/C++ 描述算法。(9 分)

查看答案与解析收起答案与解析
(1) 设计思想 [4 分]

关键洞察:BST 中离 K 最近的关键字最多只有两个。把树中所有关键字放到中序序列里就是有序数组,K 在数轴上的位置无非三种:

  • K 命中某个结点 → 距离为 0,答案唯一就是 K;
  • K 落在两个相邻关键字之间 → 答案就是这两个邻居:比 K 小的最大值(记为 floor) 和 比 K 大的最小值(记为 ceil) 之一,或两者并列;
  • K 比整棵树最小值还小(或比最大值还大)→ 只有 ceil(或只有 floor)。

为什么不需要遍历整棵树?一旦确定了 floor 和 ceil,离 K 比这两个邻居更远的所有结点都可以丢掉——不可能更近。BST 的结构刚好支持「沿一条路径」就找出 floor 和 ceil:

  • 当前结点 cur->data < K:cur 自身和它的整个左子树都 < K,但右子树里可能藏着比 cur 更接近 K(仍 < K)的结点,所以 cur 是当下最佳的 floor 候选,然后向右走继续找;
  • 当前结点 cur->data > K:对称,cur 是当下最佳的 ceil 候选,然后向左走;
  • 当前结点 cur->data == K:直接命中,距离 0,结束。

完整流程:

  1. 从根开始,用迭代方式下降;下降途中每访问一个结点就按上述规则更新 floor 或 ceil,并向对应方向走;命中则提前结束。
  2. 走到空指针时停止。此时 floor、ceil 至少有一个存在。
  3. 比较 K − floor 与 ceil − K:哪边小输出哪边;相等则两个都输出。

整条路径长度 ≤ BST 高度 h,所以时间 O(h),空间 O(1)(只用了几个标量)。

编者注(生僻术语):术语 floor (下界) 指 BST 中 ≤ K 的最大键,ceil (上界) 指 ≥ K 的最小键。这两个名字出自数学里的「向下取整 / 向上取整」,在 BST / 有序集合面试题中是高频概念,记住它能省下很多解释。

(2) 代码实现 [9 分]
c 复制代码
typedef struct BSTNode {
    int data;
    struct BSTNode *left, *right;
} BSTNode;

// 找出 BST 中与 K 之差绝对值最小的所有关键字并打印
// 输出形式:第一行为最小 |差|,第二行升序输出对应关键字
void closestKey(BSTNode *T, int K) {
    int hasFloor = 0, hasCeil = 0;     // floor / ceil 是否已经找到
    int floorV = 0,  ceilV = 0;        // 当前最佳的 floor / ceil 值
    BSTNode *cur = T;

    while (cur != NULL) {
        if (cur->data == K) {                 // 命中:差为 0,唯一答案就是 K
            printf("0\n%d\n", K);
            return;
        } else if (cur->data < K) {
            // cur 比 K 小,是更优的 floor 候选;右子树里可能还有更接近 K 的下界
            if (!hasFloor || cur->data > floorV) {
                hasFloor = 1;
                floorV = cur->data;
            }
            cur = cur->right;
        } else {                              // cur->data > K
            if (!hasCeil || cur->data < ceilV) {
                hasCeil  = 1;
                ceilV    = cur->data;
            }
            cur = cur->left;
        }
    }

    // 路径走完,根据 floor / ceil 是否存在以及谁更近输出结果
    if (!hasFloor) {                                          // 整棵树都比 K 大
        printf("%d\n%d\n", ceilV - K, ceilV);
    } else if (!hasCeil) {                                    // 整棵树都比 K 小
        printf("%d\n%d\n", K - floorV, floorV);
    } else {
        int df = K - floorV, dc = ceilV - K;
        if (df < dc)      printf("%d\n%d\n", df, floorV);
        else if (dc < df) printf("%d\n%d\n", dc, ceilV);
        else              printf("%d\n%d %d\n", df, floorV, ceilV);  // 并列双解,升序
    }
}

关键点说明:

  • 沿一条路径下降,绝不递归整棵树。考研学生最容易写的版本是「中序遍历整棵树挨个比较」——它能算出正确答案、拿到正确性那部分分,但它是 O(n)、完全没用上 BST 的性质,会丢掉最优性那部分分。本题真正想考的,就是用 BST 性质把它做到 O(h):能做对是基础,能做到最优才是这道题的区分点。
  • floor/ceil 各更新一次就够吗?够。沿路径走时,每转向右就保证 floor 在变大、每转向左就保证 ceil 在变小,所以走到底时 floor 是路径上 ≤ K 的最大者、ceil 是路径上 ≥ K 的最小者;又因为 BST 性质保证「不在路径上的结点不可能比路径上的结点更接近 K」,floor/ceil 即为全局最优。
  • 并列输出顺序:题目说「所有结点」,并列时 floor < K < ceil,所以 floorV ceilV 自然就是升序,无需额外排序。
  • 复杂度:时间 O(h),h 为树高(平均 O(log n),最坏退化成单链时 O(n));空间 O(1),只用了几个标量。如果用递归写,则会多 O(h) 的栈空间,本题用迭代更优。
进入练习

第 42 题

数据结构
10 分

栈的基本操作有出栈和入栈。将序列 1,2,3,…,n 依次入栈,回答下列问题:

(1) 当 n=9 时,可以得到出栈序列 {2,3,1,6,4,7,5,8} 吗?可以得到出栈序列 {2,3,1,4,6,5,7,8} 吗?(2 分)

(2) 假设 1,2,…,n 组成任意序列的出栈序列 P1,P2,…,P**n ,在序列中有 P**i 、 P**j 、 P**k ( i<j<k ),若该出栈序列不能由栈得到,则 P**i 、 P**j 、 P**k 的大小关系是?(2 分)

(3) 若 n=4 ,则以 2 开头的序列个数有多少个?(2 分)

(4) 若 n=k−1 时,出栈序列总共共有 M 个,如果 n=k ,那么以 1 开头的出栈序列个数有多少个?以 2 开头的出栈序列有多少个?总共的出栈序列有多少个?(4 分)

查看答案与解析收起答案与解析
(1) 出栈序列判定

答案:第一个 {2, 3, 1, 6, 4, 7, 5, 8} 不可以;第二个 {2, 3, 1, 4, 6, 5, 7, 8} 可以。

判定方法:模拟"按 1, 2, 3, ... 顺序入栈,按目标序列要求出栈"的过程,若某一步出栈要的元素不在栈顶就判失败。

序列 1 模拟(输入 1..n 依次入栈):

操作 栈状态(右为顶) 已出栈
push 1 [1] —
push 2 [1, 2] —
pop → 2 ✓ [1] 2
push 3 [1, 3] 2
pop → 3 ✓ [1] 2,3
pop → 1 ✓ [] 2,3,1
push 4 [4] 2,3,1
push 5 [4, 5] 2,3,1
push 6 [4, 5, 6] 2,3,1
pop → 6 ✓ [4, 5] 2,3,1,6
要 pop → 4,但栈顶是 5 ✗ — —

结论:序列 1 不可由栈得到。

序列 2 模拟——按相同方法逐步推,可以走通至最后 P_8 = 8 出栈,全程无矛盾。具体过程可由读者自行验证(关键点:4 直接出后再 push 5,6,先 pop 6 再 pop 5)。结论:序列 2 可以由栈得到。

(2) 不能由栈得到的序列中 P_i, P_j, P_k 的大小关系

答案:存在 i < j < k 满足 Pj<Pk<PiP_j < P_k < P_i(即 P_i 最大、P_j 最小、P_k 居中),下标递增但值呈"大-小-中"模式。

证明(反证):若出栈序列出现 i<j<k 满足 Pi>Pk>PjP_i > P_k > P_j(即 Pj<Pk<PiP_j < P_k < P_i),考察 P_i 出栈那一刻——

  • Pj>PiP_j > P_i?不,Pj<PiP_j < P_i。所以 P_j 在 1..n 中比 P_i 小,P_j 入栈在 P_i 之前(按 1..n 顺序入栈,小数先入);
  • 既然 P_j 入栈在 P_i 之前,且 P_j 在 P_i 之后才出(因为 i < j),P_i 出栈时 P_j 必在栈中且在 P_i 下方;
  • 同样地,Pk<PiP_k < P_i,P_k 也是入栈在 P_i 之前。但 Pk>PjP_k > P_j,且 P_k 比 P_j 后出(j < k),所以 P_i 出栈时 P_k 在栈中且在 P_j 上方、P_i 下方。

P_i 出栈后,栈顶是 P_k(不是 P_j,因为 P_k 在 P_j 上方)。要让 P_j 在 P_k 之前出(j < k 意味着 P_j 先出)就违反栈 LIFO——栈顶必先出,即 P_k 应先于 P_j 出栈。矛盾。

用序列 1 验证:{2, 3, 1, 6, 4, 7, 5, 8}。取 i=4, j=5, k=7(1-indexed):P4=6,P5=4,P7=5P_4=6, P_5=4, P_7=5。P5<P7<P4P_5 < P_7 < P_4(4 < 5 < 6)✓——刚好是不可由栈得到的关键三元组。

(3) n = 4 时以 2 开头的出栈序列个数

答案:5 个,分别是 {2,1,3,4}, {2,1,4,3}, {2,3,1,4}, {2,3,4,1}, {2,4,3,1}。

推导:P_1 = 2 意味着已 push 1、push 2、pop 2,当前栈 = [1],待入 = [3, 4]。从此状态出发的所有合法栈操作:

复制代码
                [1] | 待入 [3,4]
               /                \
        pop 1 (→ 21..)        push 3 (→ 23..)
        |                     |
       [] | [3,4]            [1,3] | [4]
      /        \             /          \
   ...2,1,3,4  ...2,1,4,3  pop 3      push 4
                             |          |
                           [1] | [4]   [1,3,4] | []
                           /     \      |
                       pop 1   push 4   pop 4,3,1 → 2,4,3,1
                         |       |
                  ..2,3,1,4  ..2,3,4,1

共 5 条到达终点的路径,对应 5 个序列:

# 序列
1 2, 1, 3, 4
2 2, 1, 4, 3
3 2, 3, 1, 4
4 2, 3, 4, 1
5 2, 4, 3, 1
(4) 用 M(n=k−1 时的总数)表达 n=k 的几个数量

设 n 个数依次入栈出栈的合法出栈序列总数为 CnC_n(恰好是卡特兰数 Catalan number)。题面给定 Ck−1=MC_{k-1} = M。

以 1 开头的出栈序列数

答案:M 个。

P_1 = 1 意味着 push 1 后立刻 pop 1,状态变为"栈空 + 待入 2..k"——这正是 n = k − 1 个数(2, 3, ..., k)的独立栈操作问题,序列数 = Ck−1=MC_{k-1} = M。

以 2 开头的出栈序列数

答案:M 个。

P_1 = 2 意味着 push 1、push 2、pop 2,状态:栈 = [1],待入 = [3, ..., k],还要再出 k−1 个数。

记 1 在 P_t 位置出栈(t∈{2,3,...,k}t \in \{2, 3, ..., k\})。在 P_1 与 P_t 之间出栈的 t−2 个数必为 {3, ..., t} 中元素(因为 1 在栈底,能出栈的"上层"必是 3..t);P_t 之后出栈的 k−t 个数是 t+1..k。这两段彼此独立,分别对应 Ct−2C_{t-2} 和 Ck−tC_{k-t} 种组合。求和:

以 2 开头的数=∑t=2kCt−2⋅Ck−t=∑j=0k−2Cj⋅Ck−2−j\text{以 2 开头的数} = \sum_{t=2}^{k} C_{t-2}\cdot C_{k-t} = \sum_{j=0}^{k-2} C_j\cdot C_{k-2-j}

由卡特兰数卷积恒等式 Cn=∑j=0n−1Cj⋅Cn−1−jC_n = \sum_{j=0}^{n-1} C_j \cdot C_{n-1-j},把 n=k−1n = k-1 代入即得右式 = Ck−1=MC_{k-1} = M。

与 (3) 验证:n = 4 时以 2 开头 = C3C_3 = 5 ✓。

n = k 时的总出栈序列数

答案:Ck=4k−2k+1 MC_k = \dfrac{4k-2}{k+1}\, M。

推导——卡特兰数的递推 CkC_k 与 Ck−1C_{k-1} 的比值:

CkCk−1=(2kk)/(k+1)(2k−2k−1)/k=kk+1⋅(2k)(2k−1)k⋅k=2(2k−1)k+1=4k−2k+1\frac{C_k}{C_{k-1}} = \frac{\binom{2k}{k}/(k+1)}{\binom{2k-2}{k-1}/k} = \frac{k}{k+1}\cdot\frac{(2k)(2k-1)}{k\cdot k} = \frac{2(2k-1)}{k+1} = \frac{4k-2}{k+1}

所以 Ck=4k−2k+1⋅Ck−1=(4k−2)Mk+1C_k = \dfrac{4k-2}{k+1}\cdot C_{k-1} = \dfrac{(4k-2)M}{k+1}。

直观验证(k=4,M=5):C4=145⋅5=14C_4 = \dfrac{14}{5}\cdot 5 = 14 ✓(卡特兰数 1, 1, 2, 5, 14, 42 ...)。

编者注(生僻术语):n 个数依次入栈所有合法出栈序列数 = 第 n 个卡特兰数 Cn=1n+1(2nn)C_n = \dfrac{1}{n+1}\binom{2n}{n}。这一结果有多种等价模型——括号匹配、不相交弦、二叉树形态、网格路径等。考研只考"出栈序列计数"这一种,记住 C4=14C_4 = 14、C5=42C_5 = 42 这几个常用值就能秒应对小 n 题;大 n 用递推 Ck=4k−2k+1Ck−1C_k = \dfrac{4k-2}{k+1} C_{k-1}。

进入练习

第 43 题

计算机组成原理
11 分

某 16 位计算机按字节编址,通用寄存器 R0~R15 的编号为 0~15,存储器地址为 16 位,采用定长指令字,指令格式有 R 型、I 型、M 型三种,如下表所示

R 型:R[rt] ← R[rt] op1 R[rs] 或 R[rt] ← R[rt] op1 num

bits-layout 复制代码
width: 16
fields:
  [15, 12] R型 = 0000
  [11, 8]  rt
  [7, 4]   rs或num
  [3, 0]   op1

I 型:R[rt] ← R[rt] op2 imm8

bits-layout 复制代码
width: 16
fields:
  [15, 12] op2
  [11, 8]  rt
  [7, 0]   imm8

M 型:R[0] ← M[R[15] + offset] 或 M[R[15] + offset] ← R[0]

bits-layout 复制代码
width: 16
fields:
  [15, 12] op3
  [11, 0]  offset

其中:

  • OP1 为 0001、0010 分别表示加、左移指令;
  • OP2 为 0100 表示加立即数指令;
  • OP3 为 1110、1111 分别表示取数、存数指令;
  • R[r] 表示寄存器 r 中的内容;
  • mm 表示移位位数;
  • M[addr] 表示存储器地址 addr 中的内容。

请回答下列问题:

(1) 主存单元和通用寄存器的宽度各为多少位?(2 分)

(2) op1 和 op2 的编码是否可以相同?op2 和 op3 的编码是否可以相同?(2 分)

(3) 若 R(2)=ABCDH,R(9)=F00H1,则指令 0000 0010 1001 0001 执行后,R2 和 R9 中的内容分别是多少?(2 分)

(4) 若变量 x 、 y 均为 16 位带符号整数,在存储器中依次从低地址向高地址连续存放, x 的地址在 R15 中。实现 y=16x−5 的 4 条指令 11–14 如题 43 表所示,写出 ①~④ 处的内容。(4 分)

题 43 表:

地址 内容
11 ① 0000 0000 0000
12 0000 ② 0010
13 0100 0000 ③
14 1111 ④
查看答案与解析收起答案与解析

本题考查 定长指令字下三种指令格式(R/I/M)的字段划分、操作码空间分配、指令译码与执行、以及用给定指令集实现一段表达式。四问层层递进:先从「按字节编址 + 字长」定宽度,再从「字段位置」判操作码能否复用,接着完整译码并追踪一次执行,最后做指令编码填空。

(1) 主存单元与通用寄存器的宽度 [2 分]
  • 主存单元宽度 = 8 位:题面明确「按字节编址」——每个存储器地址对应一个字节,所以一个主存单元就是 1 字节 = 8 位。
  • 通用寄存器宽度 = 16 位:本机是 16 位计算机(字长 16 位);R 型/I 型的运算结果、M 型一次取/存的数据都以「字」为单位,寄存器必须能容纳一个完整的字 → 16 位。

编者注(易错点):主存单元宽度由「编址单位」决定(按字节编址 = 8 位、按字编址 = 字长),不是字长;寄存器宽度才由字长决定。两个「宽度」问的是不同东西,别都写 16 位。

(2) op1 与 op2、op2 与 op3 能否同编码 [2 分]

先看三个操作码字段各自的位置:

操作码 所在字段 用途
op1 [3:0](前提 [15:12]=0000) R 型内部区分 加 / 左移
op2 [15:12] 标识 I 型及其操作
op3 [15:12] 标识 M 型及其操作
  • op1 与 op2 可以相同。op1 在低 4 位 [3:0],op2 在高 4 位 [15:12],分属不同字段;指令类型先由 [15:12] 判定——[15:12]=0000 才是 R 型(再看 [3:0] 的 op1),[15:12]=op2(≠0000) 是 I 型。即使 op1 与 op2 取相同的 4 位值,一个看 [3:0]、一个看 [15:12],互不干扰。
  • op2 与 op3 不可以相同。两者都占 [15:12],分别区分 I 型与 M 型;若 op2=op3,取指时读到这个 [15:12] 值就无法判断指令是 I 型还是 M 型 → 译码冲突。
  • 补充:op2、op3 还必须都 ≠ 0000(否则与 R 型前缀相撞)。题给 op2=0100、op3=1110/1111,确实互不相同且都 ≠0000 ✓。

编者注(解题套路):「操作码能否相同」一律先问「它们在不在同一字段」。同字段 → 必须互不相同(否则译码歧义);不同字段 → 可以相同(由别的字段消歧)。

(3) 指令 0000 0010 1001 0001 的执行结果 [2 分]

先译码([15:12]=0000 → R 型):

字段 [15:12]=0000 [11:8]=0010 [7:4]=1001 [3:0]=0001
含义 R 型前缀 rt = 2 → R[2] rs = 9 → R[9] op1 = 加

所以指令语义是 R[2] ← R[2] + R[9](rt 既是被加数也是结果寄存器)。

再执行:R[2]=ABCDH,R[9]=F001H,按 16 位相加(最高位进位丢弃):

复制代码
  ABCD
+ F001
------
  9BCE     D+1=E;C+0=C;B+0=B;A+F=0x19 → 留 9 进 1,进位超出 16 位被丢弃
  • R2 = 9BCEH
  • R9 = F001H(源寄存器只读,不参与写回,保持不变)

编者注(题面笔误):题面 R(9)=F00H1 应为 F001H(H 后缀位置笔误),本解析按 F001H 计算。

编者注(易错点):① A+F 产生的最高位进位在 16 位机里自动丢弃,结果取低 16 位 9BCEH,不要写成 5 位的 19BCEH;② R 型 R[rt] ← R[rt] op R[rs] 只改 rt(R2),rs(R9)只读不写。

(4) 用 4 条指令实现 y = 16x − 5 [4 分]

变量布局:x、y 都是 16 位带符号整数 = 2 字节;按字节编址、依低→高连续存放,x 的地址在 R15。所以 x 在 M[R15+0]、y 在 M[R15+2](x 占 R15 与 R15+1 两个字节,y 紧随其后)。

算法:y = 16x − 5 = (x << 4) − 5,四步——取 x → 左移 4 位(×16)→ 加 (−5) → 存到 y。

指令 语义 类型与字段 完整机器码
I1 R[0] ← M[R[15]+0](取 x) M 型取数 op3=1110,offset=0 1110 0000 0000 0000
I2 R[0] ← R[0] << 4(×16) R 型 op1=左移=0010,rt=R0,num=4 0000 0000 0100 0010
I3 R[0] ← R[0] + (−5) I 型 op2=加立即数=0100,rt=R0,imm8=−5 0100 0000 1111 1011
I4 M[R[15]+2] ← R[0](存 y) M 型存数 op3=1111,offset=2 1111 0000 0000 0010

对照填空位置:

  • ① = 1110(I1 的 [15:12],取数 op3)
  • ② = 0000 0100(I2 的 [11:4]:rt=0000 即 R0、num=0100 即移 4 位)
  • ③ = 1111 1011(I3 的 [7:0]:−5 的 8 位补码 = FBH)
  • ④ = 0000 0000 0010(I4 的 [11:0]:offset = +2,指向 y)

编者注(易错点):

  • y 的偏移是 +2 不是 +1——16 位整数占 2 字节,按字节编址下相邻整数的地址差 2。
  • −5 必须填补码并符号扩展:imm8 = 1111 1011(FBH),加立即数时符号扩展成 FFFB 再加,等价于减 5;若填 0000 0101(+5)就把减做成了加。
  • 左移的 num 是移位位数(4)直接写在 [7:4],不是寄存器号;×16 = 左移 4 位。
进入练习

第 44 题

计算机组成原理
12 分

假定 43 题中计算机 C 的部分数据通路如题 44 所示。

图片待补充

图中带箭头虚线代表控制信号,IR.rt、IR.rs 分别表示 IR 中的 rt、rs 字段,IR₁₁₋₀ 为 IR 的低 12 位,要求取指令周期完成 PC 增量操作,请回答下列问题

(1)①和②是同一类部件,其名称是什么(1 分)

(2)I 型指令中 imm8 可以是带符号或无符号整数,M 型指令中 offset 是带符号整数,则 EXTOP 至少有几位?为什么?(2 分)

(3)取指周期中 MARSrC、ALUA SrC、ALUB SrC、RegWr 的取值各是什么?(4 分)

(4)左移指令周期中 ALUB SrC、RegWsrc、RegDst、RegWr 的取值各是什么?Extop 是否可以与 M 型指令中的 EXTop 相同?为什么?(2 分)

查看答案与解析收起答案与解析

本题在 2026-43 的指令系统之上,就一个单总线 / 多路选择器风格的 CPU 数据通路考查 多路选择器识别、立即数扩展宽度、取指阶段控制信号、左移指令控制信号。

通用思路:每一类指令在每一阶段都对应一组确定的控制信号取值。读题时先想清楚"这一拍数据流向哪里",再去逐一确定每个控制信号。

控制信号速查(以图 44 约定):

信号 含义
MARSrc MAR 取值来源:PC 还是 ALU 输出
ALUASrc ALU 的 A 端:PC 还是寄存器
ALUBSrc ALU 的 B 端:寄存器 / 常数 / 立即数
RegWr 是否写寄存器(0 = 不写)
RegWsrc 写回数据来自 ALU 还是 MDR
RegDst 写到 R[rs] 还是 R[rt]
EXTop 立即数扩展模式:零扩展 / 符号扩展
(1) ① 和 ② 是什么部件 [1 分]

多路选择器(MUX)。

理由: 不同指令类型对寄存器编号的取法不同——

  • R 型指令读 / 写 rs、rt(来自 IR.rs / IR.rt 字段);
  • M 型指令固定取 R[0] 作存数 / 取数寄存器、R[15] 作基址。

需要在 "IR 字段" 与 "固定 0 / 15" 之间二选一 → 多路选择器。

(2) EXTOP 至少几位 [2 分]

1 位。

理由: 题干给出三类立即数情形:

  • I 型 imm8 当带符号 → 符号扩展;
  • I 型 imm8 当无符号 → 零扩展;
  • M 型 offset 当带符号 → 符号扩展。

总共只有 零扩展 / 符号扩展 两种模式,1 位即可编码(0 表零扩展,1 表符号扩展)。

易错点: "EXTop 几位"看的是有多少种扩展模式,不是有多少种指令。本题虽然有 R/I/M 三种指令格式,但扩展操作只有 2 种,故 1 位足够。

(3) 取指周期的 4 个控制信号 [4 分]

取指阶段要做的事:

  1. 把 PC 当作主存地址送 MAR;
  2. 读主存把指令送 IR;
  3. 同时把 PC + 2 算出,更新 PC(题目要求"取指周期完成 PC 增量")。

逐个确定:

信号 取值 理由
MARSrc 0 MAR 地址来自 PC(不是 ALU 输出)
ALUASrc 0 ALU 的 A 端用 PC(要算 PC + 2)
ALUBSrc 1 ALU 的 B 端取常数 2(PC 增量用常量)
RegWr 0 取指阶段不写通用寄存器

编者注(生僻术语): "取指期完成 PC 增量"是教学型 CPU 的常见简化——把 PC 自增的 ALU 操作和指令读取并行做,省一个时钟周期。真实 CPU 里通常用 专用 PC 加法器(不与主 ALU 共用),同样能并行。

(4) 左移指令周期的控制信号 + EXTop 是否同 M 型 [2 分]

左移指令是 R 型(OP1 = 0010,从 2026-43 (4) 推得 num 字段直接当移位位数)。

执行阶段动作:把寄存器内容左移立即数指定的位数,写回 rt。

信号 取值 理由
ALUBSrc 2 ALU B 端取扩展器输出的立即数(移位量)
RegWsrc 1 写回数据来自 ALU 输出(不是 MDR,未访存)
RegDst 1 写到 R[rt](按指令格式)
RegWr 1 必须写寄存器(结果 R[rt] ← R[rt] << num)

EXTop 与 M 型的对比:

  • M 型:offset 是带符号整数 → EXTop = 1(符号扩展);
  • 左移指令:num 字段是无符号移位位数(从来不是负移位)→ EXTop = 0(零扩展)。

结论:左移指令与 M 型的 EXTop 不同。 左移用零扩展,M 型用符号扩展。

易错点: "立即数扩展"不是只有一种——要看立即数的语义。地址偏移可正可负,必须符号扩展;移位位数恒为非负,零扩展即可。两者使用同一个扩展器但 EXTop 取值相反。

进入练习

第 45 题

操作系统
7 分

(本题满分 7 分)

系统采用优先级(优先级值越大优先级越高)+ 时间片轮转调度算法。规则:

  • 仅当发生时钟中断时才触发抢占 CPU 的操作
  • 时钟中断间隔 = 10 ms
  • 进程首次进入就绪队列时,其时间片 = 50 ms
  • 若进程因时间片用完而返回就绪队列,其优先级值 − 1
  • 若进程被更高优先级抢占而返回就绪队列,其优先级值保持不变
  • 多个进程优先级相同时,先进入就绪队列的进程优先

四个进程的到达时刻、初始优先级、CPU 总运行时间如下:

进程 到达就绪队列时间 (ms) 优先级 CPU 运行时间 (ms)
P1 10 3 95
P2 10 4 20
P3 12 2 40
P4 14 5 60

(1) 从 10 ms 开始进程调度,直至所有进程调度结束,中断次数 与 CPU 调度次数 各为多少?P1、P2、P3、P4 的首次调度发生在哪个时刻?(5 分)

(2) 若时间片由 50 ms 改为 100 ms,CPU 调度次数将增大、不变还是减少?若时钟中断间隔由 10 ms 改为 1 ms,系统开销将增大、不变还是减少?(2 分)

查看答案与解析收起答案与解析
(1)逐时刻推演

关键约束(请反复看 3 遍):抢占只在时钟中断(每 10 ms)时发生。所以"P3 在 t=12 到达"、"P4 在 t=14 到达"这种非中断时刻只让进程进就绪队列,不会立刻抢 CPU。

完整时间线
时刻 事件 调度动作 此后 CPU 运行 剩余 CPU 时间
10 P1、P2 到达;CPU 空闲 调度 P2(P2 prio=4 > P1 prio=3) P2 P2: 20→
12 P3 到达 非时钟中断,不调度 P2 仍跑
14 P4 到达 非时钟中断,不调度 P2 仍跑
20 时钟中断 P4 抢占 P2(P4 prio=5 最高);P2 优先级不变为 4 P4 P2: 10 / P4: 60→
70 时钟中断 P4 时间片用完(连跑 50 ms),prio 5→4。就绪队列里 P2、P4 同 prio=4,P2 先入队 → 调度 P2 P2 P4: 10
80 时钟中断 P2 跑完 10 ms,总共 10+10=20 ms = 完成。就绪队列剩 P1(3)、P3(2)、P4(4) → 调度 P4 P4
90 时钟中断 P4 跑完 10 ms,总共 50+10=60 ms = 完成。就绪队列剩 P1(3)、P3(2) → 调度 P1 P1 P1: 50
140 时钟中断 P1 时间片用完(连跑 50 ms),prio 3→2。就绪队列里 P1、P3 同 prio=2,P3 先入队(t=12 入队,P1 才刚降级)→ 调度 P3 P3 P1: 45
180 时钟中断 P3 跑完 40 ms = 完成。就绪队列剩 P1(2) → 调度 P1 P1 P1: 45→
225 P1 跑完剩 45 ms = 完成(225 < 235,不会再触发时间片) 全部结束 — —

CPU 占用时序图(t=0 到 t=225):

process-gantt 复制代码
total: 225
schedule:
  idle @ 0-10
  P2 @ 10-20
  P4 @ 20-70
  P2 @ 70-80
  P4 @ 80-90
  P1 @ 90-140
  P3 @ 140-180
  P1 @ 180-225
答案统计
  • 时钟中断次数:从 t=10 起到 t=225,每 10 ms 一次,依次是 10、20、30、…、220 共 22 次
  • CPU 调度次数:发生在 t = 10、20、70、80、90、140、180 共 7 次
  • 首次调度时刻:
进程 首次调度时刻 (ms) 备注
P1 90 P4 完成后才轮到,因为优先级低于 P2 / P4
P2 10 起始就被调度,最高优先级在线者
P3 140 排到最后,优先级 2 最低
P4 20 到达后第一个时钟中断就抢到 CPU

编者注(易错点 / 答题套路):

  • "非中断时刻不抢占" 是本题最关键的设定。如果忘了它,会以为 P3 t=12 到达就能立即抢,导致整个时间线偏移。
  • "被抢占时优先级不变" vs "时间片用完时优先级 −1" 必须分清——P2 在 t=20 被 P4 抢走,prio 仍是 4;P4 在 t=70 时间片用完,才降到 4。同样 P1 在 t=140 用完时间片才从 3 降到 2。
  • 同优先级 FCFS 要看"哪个先入就绪队列"。t=70 时 P2 是 t=20 入队、P4 是刚刚入队,P2 优先;t=140 时 P3 是 t=12 入队、P1 是刚被踢出 CPU 入队,P3 优先。这两次"先后顺序"判定是本题易错的重灾区。
  • 首次调度 ≠ 首次进入就绪队列。P3 在 t=12 就进队了,但首次拿到 CPU 是 t=140,相差 128 ms。
(2)参数变化对调度次数 / 系统开销的影响
① 时间片 50 ms → 100 ms:CPU 调度次数 减少

时间片变长 → 进程"因时间片用完而让出 CPU"的次数减少,每个进程一次能跑得更久 → 整个推演里被时间片切走的事件变少。

举例:本题里 P4 时间片用完 (t=70)、P1 时间片用完 (t=140) 这两次事件,若时间片是 100 ms,则 P4 跑 50 ms 就刚好完成(不会触发时间片用完);P1 也类似。

② 时钟中断间隔 10 ms → 1 ms:系统开销 增大

每次时钟中断都要:保存现场 → 进入中断处理 → 检查是否需要调度 → 恢复现场(或切换进程)。间隔从 10 ms 缩到 1 ms 意味着 1 秒内中断从 100 次飙到 1000 次,纯中断处理与上下文检查的开销会显著增加,CPU 实际"干活时间"被吞掉一部分。

编者注(直觉解释):

  • 时间片短:响应快、上下文切换多 → 适合交互式系统
  • 时间片长:吞吐高、响应慢 → 接近批处理
  • 中断间隔短:调度精度高、开销大
  • 中断间隔长:调度粒度粗、开销小

这是 OS 章节"时间片 vs 中断频率"经典权衡,记住"短=灵活/费、长=稳定/省"四个字就能秒答这类对比题。

进入练习

第 46 题

操作系统
8 分

(本题满分 8 分)

文件系统的目录项包括文件名和索引节点号。磁盘包含索引节点表、位图、目录、文件数据等元数据。设:

  • 盘块大小 = 4 KB,盘块号占 4 B
  • 索引节点表存放系统所有文件,从 0 开始编号,存放在盘块号 100 开始连续的 4096 个盘块中
  • 索引节点占用 128 B,包含直接地址项 5 个 + 一级间接地址项、二级间接地址项、三级间接地址项各 1 个
  • 磁盘位示图 / 索引节点位示图分别记录磁盘 / 索引节点的使用情况,0 = 未使用、1 = 已使用

目录结构与各文件的索引节点号如下图所示,file 文件占 30 KB。

file-system-tree 复制代码
layout: chain
tree:
  dir [dir]: dir1
  dir1 [dir]: file
  file
columns: 文件, 索引节点号
rows:
  dir | 100
  dir1 | 201
  file | 1000

(1)file 的索引节点所在的盘块号是多少?若 file 的索引节点已经读取到内存,要访问 file 文件中偏移地址 21460 的一个字节数据,则最多需要读多少个盘块?如果文件系统中有足够的磁盘空间,则最多可以存放多少个文件?(3 分)

(2)如果要删除目录 dir1,则需要对元数据进行哪些操作?(5 分)

查看答案与解析收起答案与解析
(1)三小问拆解
① file 的索引节点所在盘块号

每个盘块能装多少个索引节点?

盘块大小每个 inode 大小=4096128=32 个 inode/块\frac{\text{盘块大小}}{\text{每个 inode 大小}} = \frac{4096}{128} = 32 \text{ 个 inode/块}

索引节点表从盘块号 100 开始,inode 编号从 0 开始。inode 1000 在第几个盘块?

1000÷32=31 余 81000 \div 32 = 31 \text{ 余 } 8

也就是说 inode 1000 是第 31 个盘块的第 8 个 inode(块内编号从 0 开始)。所以盘块号是:

100+31=131100 + 31 = \boxed{131}

编者注(易错点):1000 mod 32 = 8 是块内的偏移编号,不是字节偏移。要换成字节偏移得乘 128,即第 8 × 128 = 1024 字节起。这一步不影响答盘块号,但若题目还问"在该盘块的第几个字节"就要算清楚。

② 访问偏移 21460 的字节最多读几个盘块

Step 1:偏移 21460 在 file 的第几个逻辑块?

⌊214604096⌋=5(因为 5×4096=20480≤21460<24576)\left\lfloor\frac{21460}{4096}\right\rfloor = 5 \quad (\text{因为 } 5\times 4096 = 20480 \le 21460 < 24576)

所以是 file 的逻辑块 5(从 0 开始编号)。

Step 2:逻辑块 5 该走哪一级寻址?

逻辑块号区间 走哪级寻址 取数据要读几个盘块
0 ~ 4 直接地址项(5 个) 1(数据块)
5 ~ 5+1024-1 一级间接 2(一级间接表块 + 数据块)
5+1024 ~ 5+1024+1024² 二级间接 3
更高 三级间接 4

逻辑块 5 正好是一级间接的第 0 项——前 5 个块(0~4)已经被直接地址项管完了,第 6 块(编号 5)必须经过一级间接索引。

Step 3:题面说 inode 已在内存 → inode 那一步不用读盘。但一级间接表块仍在磁盘上,必须读出来才能查到逻辑块 5 对应的实际盘块号;接着再读数据块本身。

最终:最多需要读 2 个盘块(一级间接表块 + 数据块)。

编者注(易错点):

  • 不要漏算"读一级间接表块"——它本身也是磁盘上的盘块。inode 在内存只是省了"读 inode"那一步
  • 也不要多算 inode——题面说了它在内存里
  • "最多"两个字也很关键。如果一级间接表所在盘块碰巧也已在内存,那只需 1 个;题目问的是 worst case
③ 最多可存放多少个文件

每个文件(含目录)占 1 个 inode。inode 总数:

4096 块×32 个/块=131072 个 inode4096 \text{ 块} \times 32 \text{ 个/块} = 131072 \text{ 个 inode}

所以最多可存放 131072 个文件。

编者注(易错点):题面问的是"文件总数"上限,受 inode 表容量约束(每个文件至少 1 个 inode),不受数据空间约束(题目说"足够的磁盘空间")。看到"足够磁盘空间"几个字立刻知道瓶颈在 inode 数量,别去算簇能装下多少 4 KB 文件。

(2)删除目录 dir1 要做哪些元数据操作

dir1 是非空目录(里面还有 file),所以先递归删除内部文件,再删自己。

Step 1:删除 file(inode 号 1000)
操作 影响的元数据
释放 file 的所有数据块(5 个直接块 + 间接块本身 + 间接块指向的数据块) 磁盘位示图 对应位 1→0
注销 inode 1000 索引节点位示图 第 1000 位 1→0
把 dir1 目录里"file → 1000"那一项删掉 dir1 的目录数据块 改写
Step 2:删除 dir1(inode 号 201)
操作 影响的元数据
释放 dir1 自身的目录数据块(存目录项的那些块) 磁盘位示图 对应位 1→0
注销 inode 201 索引节点位示图 第 201 位 1→0
把 dir 目录里"dir1 → 201"那一项删掉 dir 的目录数据块 改写
file-system-tree 复制代码
layout: chain
tree:
  dir [dir]: dir1
  dir1 [dir]: file
  file
columns: 文件, 索引节点号
rows:
  dir | 100
  dir1 | 201
  file | 1000
highlight:
  nodes: dir1, file
  rows: dir1, file

编者注(易错点 / 答题套路):

  • 递归删除是关键。若答"直接删 dir1"会丢分——目录非空时要先把孩子清空。这也是 Unix rm 默认禁止删非空目录的原因(rm -r 才行)。
  • 三类元数据要同时考虑:
    1. 数据块归还(磁盘位示图)
    2. inode 归还(索引节点位示图)
    3. 父目录里的目录项删除(数据块改写)
      漏掉任意一类都会扣分,建议答题时按这三大类逐项展开。
  • "间接块本身"不要忘——它也占着盘块,删 file 时要一并归还。同理,二级 / 三级间接还要释放更多层级的间接块。
进入练习

第 47 题

计算机网络
9 分

(本题满分 9 分)

假设客户端 C 建立一条 TCP 连接,向服务器 Si 上传一个总长度为 2000 B 的计算任务描述文件。已知 C 的拥塞窗口初始阈值为 8 MSS,MSS = 500 B,Si 对收到的每个 TCP 段进行确认,且确认段不封装数据。接收窗口始终为 1000 B,RTT = 5 ms,C 建立连接时选择的初始序号为 1000,Si 选择的初始序号为 2000,SYN、ACK、FIN 为标志位,seq 为序号,ack_seq 为确认序号。在整个文件传输过程中未出现任何重传或报文丢失。

network-topology 复制代码
nodes:
  C [host] @ (0, 1.4) | "C"
  Platform [server] @ (1.5, 1.4) | "算力调度平台"
  S1 [server] @ (3, 0) | "S1"
  Si [server] @ (3, 1.4) | "Si"
  Sn [server] @ (3, 2.8) | "Sn"
edges:
  C -- Platform
  Platform -- S1
  Platform -- Si
  Platform -- Sn

(1)C 与 Si 建立 TCP 连接过程需要几次握手?C 收到的 SYN = 1、ACK = 1 的 TCP 段的确认序号是多少?

(2)当 C 接收 Si 发送的 ACK = 1,seq = 2001,ack_seq = 2001,rwnd = 1000 确认段后,C 的拥塞窗口增加到多少?C 的发送窗口设置为多少?

(3)C 与 Si 释放 TCP 连接过程需要几次挥手?C 收到最后一个 TCP 报文段的序号(seq)、确认序号(ack_seq)、FIN 的值分别是多少?

(4)忽略报文段传输时延,且时间从 C 请求建立 TCP 连接时刻算起,则 C 确定 Si 已成功接收到文件的时间是多少?

查看答案与解析收起答案与解析
(1) 三次握手 + 第二次握手的确认序号

TCP 建立连接需要 3 次握手:

复制代码
1️⃣ C → Si    SYN=1,seq=1000
2️⃣ Si → C    SYN=1,ACK=1,seq=2000,ack_seq=1001
3️⃣ C → Si    ACK=1,seq=1001,ack_seq=2001

C 收到的"SYN=1、ACK=1"段就是第二次握手的报文。它的确认序号是对 C 第一次握手 SYN 的确认 = 1000+1=10011000 + 1 = \mathbf{1001}。

SYN 段不携带数据但消耗 1 个序号,所以 ack_seq 是 SYN 段 seq + 1 而不是直接等于。FIN 段同理也消耗 1 个序号。

(2) 收到 ack_seq = 2001 后的窗口

先解读 ack_seq = 2001 是哪个 ACK:

  • 题面说"Si 发送的 seq = 2001、ack_seq = 2001"——Si 的 seq 从 2000 起步加 1(SYN 消耗),所以 seq = 2001 是 Si 第二次握手后的下一段,即 数据传输阶段的第一个 ACK
  • 这个 ACK 是 Si 收到 C 第一段数据(500 B)后回的——它说"我已收到 [1001, 1500],下一字节期待 1501"——但题面给的 ack_seq = 2001 这个数让我们推算 Si 已收的字节数

仔细理解 ack_seq = 2001:

实际上,如果是 C 发的第 1 个数据段(seq=1001, len=500)的 ACK,应该是 ack_seq=1501;ack_seq=2001 意味着 Si 已收到 [1001, 2000],即 2 个 MSS = 1000 B = 2 个段的数据。所以这是 C 已收到的第 2 个 ACK。

题面文字描述的"该确认段"实际上是从 C 的视角看到的"某个收到的"确认段,编号是相对的。从内容看 ack_seq=2001,明确说明是 Si 累计确认到 2000 之时——对应 C 已发送 2 段后收到的第 2 个 ACK。

慢启动阶段(cwnd < ssthresh = 8 MSS 期间),每收一个 ACK,cwnd += 1 MSS:

时刻 cwnd 起始 收 ACK 后 cwnd
收第 1 个 ACK 1 MSS 2 MSS
收第 2 个 ACK(即 ack_seq=2001 这次) 2 MSS 3 MSS = 1500 B

发送窗口 = min(cwnd, rwnd) = min(1500 B, 1000 B) = 1000 B

rwnd 经常成为瓶颈:本题 rwnd 固定 1000 B(接收方告诉发送方"最多再给我 1000 B"),cwnd 即便涨到 8 MSS 也用不上——发送窗口被 rwnd 死死卡在 1000 B。这种情况下慢启动/拥塞避免的算法效果失效,要靠应用层主动取走数据让 rwnd 抬升。

(3) 四次挥手 + 最后一段的字段

TCP 释放连接需要 4 次挥手:

复制代码
1️⃣ C → Si    FIN=1,seq=3001
2️⃣ Si → C    ACK=1,seq=2001,ack_seq=3002
3️⃣ Si → C    FIN=1,ACK=1,seq=2001,ack_seq=3002  ← C 最后收到的报文段
4️⃣ C → Si    ACK=1,seq=3002,ack_seq=2002

算 C 的 FIN seq = 3001:

C 的初始 seq=1000,SYN 占 1 → 数据从 1001 开始,发了 2000 B → 最后一字节 seq=3000。FIN 段紧接其后 → seq=3001。

Si 发的 FIN seq = 2001:

Si 没发数据,自己的序号始终在 2001(SYN+1)原地未动。

ack_seq = 3002:

Si 第二次挥手的 ACK 已确认了 C 的 FIN(ack_seq = 3001 + 1 = 3002)。第三次挥手时同样填这个值。

C 收到的最后一个报文段(即第三次挥手):

  • seq = 2001
  • ack_seq = 3002
  • FIN = 1

为什么挥手是 4 次而握手是 3 次:握手时服务器把"对客户端 SYN 的 ACK"和"自己的 SYN"合并成一段(SYN+ACK),所以 3 次。挥手时服务器收到 FIN 后可能还有数据要发——必须先 ACK 让客户端知道"FIN 收到了",等数据发完才发自己的 FIN,两个段不能合并。本题 Si 没数据,但 4 次挥手是 TCP 标准定义的,不会因为没数据就压缩成 3 次。

(4) C 确认 Si 接收完文件的时间

先列时间线(题面"忽略报文段传输时延"意味着段在链路上花 0 时间,但 RTT 仍是 5 ms 表示"信号往返一趟"):

时刻 事件
t = 0 C 发 SYN(请求建立连接)
t = 1 RTT = 5 ms C 收到 SYN+ACK,立即发 ACK(建立完成),同时发 第 1 段数据
t = 2 RTT = 10 ms C 收到第 1 段的 ACK(ack_seq=1501),cwnd 变 2 MSS;同时 rwnd 给的发送窗口允许发送 2 段 — 但本轮 rwnd=1000 限制发 2 段(第 2、3 段)
t = 3 RTT = 15 ms C 收到第 2、3 段的 ACK,cwnd 变 4 MSS;rwnd 仍 1000 限制 → 发 第 4 段(最后一段)
t = 4 RTT = 20 ms C 收到第 4 段的 ACK → C 确定 Si 已成功接收完所有 4 段 = 整个文件

总耗时 = 4 RTT = 4 × 5 ms = 20 ms。

"忽略传输时延" 在时间线上的等价说法:把每个报文段在链路上看作"瞬时到达",所以 C 发完一段 / 一组到下一组到达的间隔正好是 RTT。RTT 含两个方向的传播——"发出去 0.5 RTT 到对端 + 对端 ACK 0.5 RTT 回来"。

本题的窗口动态:表面看是慢启动(cwnd 每 RTT 翻倍:1→2→4→8),但 rwnd=1000 B 把发送窗口卡在 2 MSS 后就涨不动了——所以从第 2 轮 RTT 起每轮都是 2 段(直到剩余字节数小于 2 段为止)。rwnd 才是真正的瓶颈,不是 cwnd。

进入练习