编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机的原理
  • 操作系统原理
  • 网络协议原理
  • 数据库的原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 技能之旅
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Kotlin精通
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机的原理
  • 操作系统原理
  • 网络协议原理
  • 数据库的原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 技能之旅
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Kotlin精通
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • 计算机基础
  • 计算机的原理

    • README
    • 01.计算机组成结构原理
    • 02.计算机存储器的原理
    • 03.计算机基础CPU设计
    • 04.系统CPU缓存的设计
    • 05.计算机输入输出设备
    • 06.计算机总线系统设计
    • 07.计算机指令编程原理
    • 08.计算机程序如何执行
    • 09.计算机内存设计原理
    • 10.计算机二进制和字节
    • 11.计算机异常处理机制
    • 12.计算机IO操作和原理
    • 13.计算机组成原理总结
    • 14.组成原理思考题答案
      • 第 01 篇·计算机组成结构原理
        • 基础思考题
        • 进阶思考题
      • 第 02 篇·计算机存储器的原理
        • 基础思考题
        • 进阶思考题
      • 第 03 篇·计算机基础 CPU 设计
        • 基础思考题
        • 进阶思考题
      • 第 04 篇·系统 CPU 缓存的设计
        • 基础思考题
      • 第 05 篇·计算机输入输出设备
        • 基础思考题
      • 第 06 篇·计算机总线系统设计
        • 基础思考题
      • 第 07 篇·计算机指令编程原理
        • 基础思考题
      • 第 08 篇·计算机程序如何执行
        • 基础思考题
      • 第 09 篇·计算机内存设计原理
        • 基础思考题
      • 第 10 篇·计算机二进制和字节
        • 基础思考题
      • 第 11 篇·计算机异常处理机制
        • 基础思考题
      • 第 12 篇·计算机 I/O 操作和原理
        • 基础思考题
      • 自测:全书掌握度检查
  • 网络协议原理

  • 操作系统原理

  • 数据库的原理

  • 计算机
  • 计算机的原理
杨充
2017-07-08
目录

14.组成原理思考题答案

# 14.组成原理思考题答案

覆盖 12 篇全部思考题。基础题给结论,进阶题给推导,动手题给思路框架。


# 第 01 篇·计算机组成结构原理

# 基础思考题

Q1:五大部件对号入座

硬件 部件归属
机械硬盘 / SSD 存储器(外存/辅存)
RAM 存储器(内存/主存)
CPU 的 ALU 运算器
CPU 的程序计数器 PC 控制器
键盘 输入设备
打印机 输出设备
触摸屏 既是输入又是输出(双向设备)
网卡 既是输入又是输出(发送=输出,接收=输入)
GPU 独立部件,核心是运算器(大量并行 ALU)+ 显存

Q2:为什么程序必须先加载到内存才能运行?

CPU 通过地址总线访问内存,指令和数据都存放在内存中,CPU 的 PC(程序计数器)只指向内存地址。如果允许 CPU 直接读硬盘:① 硬盘速度 ~10ms,内存 ~100ns,CPU 要等 10 万倍的时间——CPU 99.999% 时间在空转;② 硬盘没有统一的地址空间(涉及文件系统、LBA 扇区号),CPU 无法直接用"地址"寻址。

内存充当了速度缓冲层和统一地址空间。操作系统通过虚拟内存让硬盘上的程序"看起来"在内存里,需要时再换入。

Q3:指令周期四个阶段

a = b + c 的五大部件数据流:

取指(Fetch):PC → MAR → 内存 → MDR → IR(控制器主导)
解码(Decode):IR 操作码 → CU 解析(控制器)
执行(Execute):CU 控制 ALU,b → ACC,c → X,ACC + X = 结果(运算器)
写回(Write Back):结果 → MDR → MAR → 内存地址 a(运算器→控制器→存储器)
PC+1 → 下一条指令

参与者:控制器(全程指挥,PC/MAR/MDR/IR/CU)、存储器(存指令和操作数)、运算器(ALU 做加法)。

Q4:冯·诺依曼 vs 哈佛架构

冯·诺依曼 哈佛
存储 指令+数据共用存储器 指令/数据分开存储
总线 一条总线,串行访问 两条总线,可并行
瓶颈 取指和取数冲突 硬件更复杂

现代 CPU L1 用哈佛(I-Cache + D-Cache 并行访问,因为取指令和读数据是流水线里不同阶段经常同时发生的操作),内存层用冯·诺依曼(统一 DDR 降低成本,且 L2/L3 统一缓存桥接了两套 L1)。

# 进阶思考题

Q1:Amdahl 定律计算

P=0.8,N=4→32:

  • 4 核:S = 1/((1-0.8)+0.8/4) = 1/(0.2+0.2) = 2.5x
  • 32 核:S = 1/((1-0.8)+0.8/32) = 1/(0.2+0.025) ≈ 4.44x

如果把 P 从 80% 优化到 95%(P=0.95):

  • 4 核:S = 1/(0.05+0.95/4) ≈ 3.48x
  • 32 核:S = 1/(0.05+0.95/32) ≈ 12.55x

结论:优化串行部分(从 20% 到 5%)比堆 28 个核效果好得多(12.55x vs 4.44x)。

Q2:功耗墙与多核

P = C × V² × f。主频 f 从 3GHz → 6GHz,要稳定还需要提高电压 V(否则信号失真),因此功耗至少翻 4-8 倍。500W+ 的 CPU 无法用常规风冷散热,这就是"功耗墙"。

big.LITTLE:日常任务跑小核(低功耗 A55 ~0.5W),重任务跑大核(A78 ~2W),平均功耗远低于全大核。更关键的是——大小核可以同时跑,小核处理后台任务时大核休眠,总吞吐不变但功耗减半。

Q3:冯·诺依曼瓶颈的三种对策

对策 原理 代价
缓存层级(L1/L2/L3) 把热数据拉到 CPU 门口 晶体管面积 + 功耗
预取(Prefetch) 推测未来访问,提前搬数据 误预取浪费带宽
乱序执行 等数据时先执行其他指令 复杂度 + Spectre 类安全漏洞
多通道内存 并行访问多个内存通道 主板布线 + 成本

Q4:给老板的结构化回答

"老板,这次事故的根因不是硬盘坏,是我们对 I/O 路径没有监控。我建议三件事:① 核心接口加 I/O 延迟埋点,磁盘慢的时候接口自动降级而不是全挂(用内存缓存兜底);② 梳理所有"读小文件"的地方改成预读或合并读,减少 I/O 次数;③ 每次发布前跑一遍 I/O 压测。这样下次磁盘慢之前我们就能先知道。"


# 第 02 篇·计算机存储器的原理

# 基础思考题

Q1:速度台阶

层级 延迟(ns) 相邻差距
寄存器 ~0.3ns 基准
L1 Cache ~1ns 3x
L2 Cache ~5ns 5x
L3 Cache ~15ns 3x
内存 DRAM ~100ns 6-7x
SSD ~100,000ns 1000x
HDD ~10,000,000ns 100x

"数量级跃升"的根本原因:物理介质变了。寄存器→L3 是 SRAM,DRAM 是电容,SSD 是 NAND,HDD 是机械。每换一种物理介质,延迟就跳一个数量级。

Q2:SRAM vs DRAM

维度 SRAM DRAM
晶体管/bit 6 个 1T+1C
速度 ~1ns ~100ns
功耗 高(始终通电保持状态) 低(只需刷新)
成本 极高 低
密度 低 高

SRAM 不能做 8GB 内存条的原因:8GB 的 SRAM 需要 8×8×6 ≈ 3840 亿个晶体管,面积和功耗大到不可接受。

Q3:断电就丢的是谁

  • 会丢:CPU 寄存器、L1 Cache、DDR 内存、Page Cache、eMMC Write Buffer(易失性)
  • 不丢:NAND 闪存颗粒(SSD/eMMC 的核心存储)、机械硬盘盘片(磁性介质)

陷阱:eMMC 的 Write Buffer 是 SRAM,断电即丢;但 NAND 颗粒是非易失的。这就是为什么突然断电可能导致"写了一半"的数据丢失。

Q4:局部性原理举例

  • 时间局部性:for(i=0;i<N;i++) sum+=arr[i] 中的 sum 变量——每次迭代都被访问
  • 空间局部性:for(i=0;i<N;i++) arr[i]=0 ——地址连续,Cache Line 预取全覆盖
  • 违反局部性:跳表/链表随机访问——node=node->next 每次跳转地址不可预测,几乎每次 Cache Miss

# 进阶思考题

Q1:本地 KV 存储方案权衡

方案 不丢数据 不卡主线程 适用场景
commit() ✅(同步 fsync) ❌ 卡主线程 关键配置,量小
apply() ❌(只到 Page Cache) ✅ 高频、可容忍少量丢失
mmap+msync ✅(msync 可控) ✅ 中等量、需要零拷贝
SQLite WAL ✅(WAL 先写日志) ✅ 需要事务、查询能力

最佳实践:高频写用 WAL(先写顺序日志,再异步合并),读用 mmap(零拷贝)。

Q2:apply() vs commit() 的底层

两者最终都用 fsync 或等效机制落盘。区别在什么时候调用 fsync:

  • commit():写 Page Cache → 立即 fsync → 等磁盘确认 → 返回(同步)
  • apply():写 Page Cache → 返回 → 后台异步 fsync(异步)

最终落盘没有本质区别——都是 fsync。但如果 apply() 还没执行到后台 fsync 就断电,数据就丢了。

Q3:mmap 不适合的场景

  1. 小文件频繁改写:mmap 的页映射粒度为 4KB,改写 1 字节也要刷整页
  2. 网络文件系统(NFS):msync 在网络文件系统上的语义不保证
  3. 多进程并发写同一文件:需要额外的同步机制,不如用 pwrite
  4. 数据量超过虚拟地址空间:32 位程序最多 4GB 地址空间

Q4:MySQL 如何用 Page Cache 且不丢数据

MySQL(InnoDB)的保证链条:WAL(Write-Ahead Log)→ fsync 日志文件 → 数据写入 Page Cache → 异步刷盘。即使断电,重启后从 WAL 日志重放恢复。

  • innodb_flush_log_at_trx_commit=1:每次事务提交都 fsync redo log(最安全)
  • Double Write Buffer:防止页部分写入(页=16KB,磁盘原子写入可能是 512B)

Q5:硬件用伪 LRU 而软件用真 LRU

硬件约束:SRAM 面积极其昂贵,每个 Cache Line(64B)如果用真 LRU 记录访问顺序需要的位数太多。伪 LRU 用二叉树实现,8 路组相联只需 7 bit。软件层(Redis)内存大,可以维护双向链表 + 哈希表实现精确 LRU,开销可接受。


# 第 03 篇·计算机基础 CPU 设计

# 基础思考题

Q1:count++ 拆成几条指令?

mov eax, [count_addr]    ; ① 从内存取 count 到寄存器(Load)
add eax, 1               ; ② ALU 加 1
mov [count_addr], eax    ; ③ 写回内存(Store)

三条指令,每条之间有"窗口期"。如果两个线程各自执行了步骤①读到相同的旧值,各自加 1 后写回——两次写回相同的"新值",丢了一次加 1。不是原子的因为 Load-Add-Store 是三个独立的指令周期。

Q2:CISC vs RISC——Intel 为什么不一刀切?

保留 CISC 外壳有三个理由:

  1. 二进制兼容性:几十年的 x86 应用不能重新编译——AMD64 兼容 IA-32 是 x86 最核心的资产
  2. 代码密度:CISC 变长指令平均代码更紧凑(I-Cache 压力小)
  3. μops 融合:复杂指令解码时可以在 μops 层做融合优化(如 load+add → 一条 μop),反而比纯 RISC 更高效

Q3:功耗墙

P ∝ V² × f。f 翻倍需要提高 V 来驱动更高的开关速度(否则上升沿不够陡),导致功耗增长远超线性。3GHz→6GHz,功耗≈4-8x。而散热能力有物理上限(风冷<200W,水冷<500W)。

Q4:MESI 四状态对号入座

场景 MESI 状态
刚 new 出来的对象 E(Exclusive)——只有当前核有,且与内存一致
只读共享的配置 S(Shared)——多核读取,与内存一致
正在被当前线程高频修改的计数器 M(Modified)——只有当前核有,已修改,内存过期
被其它线程写过的过期数据 I(Invalid)——当前核的副本已失效,下次访问要重新从其他核/内存取

Q5:伪共享判定

  • 两个线程写同一个对象的两个 long → 伪共享(两个字段在同一缓存行 64B 内)
  • 两个线程写 long[] 的不同下标 → 如果下标差<8(64/8)则伪共享
  • 两个线程写不同对象的各自 long 字段(对象连续分配)→ 伪共享(对象在堆上连续分配,字段间距可能 <64B)

核心判断:两个写发生的物理地址间距 < 64 字节 = 伪共享。

# 进阶思考题

Q1:CAS 不是"免费的原子"

synchronized 底层是 monitorenter → 对象头锁标记,持锁失败→线程挂起(OS 调度开销)。AtomicLong 的 CAS 不自旋挂起,但在高竞争下 lock cmpxchg 指令反复失败→自旋,这时候:

  • 每个 CAS 都触发 MESI 的 Invalidate 消息,缓存行在多核间弹跳——每次 CAS 的实际延迟从 ~10ns 变成 ~200ns
  • 10 个线程同时 CAS 时 9 个失败、全部重试——CPU 在空转

此时 synchronized 更好:挂起线程释放 CPU,其他线程可以做有用工作。

Q2:LongAdder 什么时候比 AtomicLong 慢?

  1. 低并发(<2 线程):Cell 数组的初始化开销 + 额外内存 = 净损耗
  2. 频繁调用 sum():需要遍历所有 Cell 并原子累加(高竞争下 cells 可能很多)
  3. 内存占用:@Contended 让每个 Cell 占至少 64B(甚至 128B),32 个 Cell = 4KB

Q3:volatile 和内存屏障

x86:TSO(Total Store Order)模型,只需 StoreLoad 屏障(mfence 或 lock 前缀)。 ARM:弱内存模型,需要 dmb 全屏障。

Java volatile 在 x86 上编译为 lock addl $0, (%rsp)(一种"空操作"的 lock 前缀,达到 StoreLoad 屏障效果)。


# 第 04 篇·系统 CPU 缓存的设计

# 基础思考题

Q1:为什么遍历顺序影响性能?

按行遍历 for(i) for(j) arr[i][j]:访问地址连续(arr[i][0], arr[i][1], ...),每次 Cache Miss 后缓存行预取 64B(16 个 int),后续 15 个命中——命中率 94%。

按列遍历 for(j) for(i) arr[i][j]:访问地址跳跃(每次跳一行,典型 40KB),每跳都 Cache Miss,且下次不会被预取——命中率接近 0%。

Q2:全相联 vs 组相联——为什么不高比例用全相联?

全相联 组相联(8路)
冲突缺失 0% 极少
查找硬件 每行一个比较器(N个) 每组 8 个比较器
能耗 极高 低

全相联的"每个地址跟所有行同时比较"需要 N 个比较器并行工作——N=1024 时需要 1024 个比较器,功耗和面积爆炸。8 路组相联在冲突率和硬件成本之间找到最优平衡。

Q3:Cache Line 64B——小了浪费、大了浪费

小(32B):突发传输未充分利用(DDR Burst Length=8 × 8B=64B);空间局部性覆盖不足。 大(128B):误预取更多无用数据,污染缓存;真共享冲突(两个核访问不同变量但落同一行概率更高)。

Q4:矩阵乘法优化原理

  1. 循环交换:让最内层循环按行而非按列访问——空间局部性
  2. 分块(Tiling):把大矩阵切成 L1 能装下的小块(如 64×64),小块内的数据在 L1 被反复用——时间局部性
  3. SIMD:一次加载 4 个 float,一次乘加 4 个——指令级并行
  4. 预取:_mm_prefetch 提前拉下一块——隐藏内存延迟

# 第 05 篇·计算机输入输出设备

# 基础思考题

Q1:四种 I/O 控制方式的区别

方式 CPU 参与 CPU 利用率 适用
程序查询 全程轮询 0%(空转) 极其简单的嵌入式
中断驱动 响应中断 高 键盘、鼠标
DMA 只发指令 最高 硬盘、网卡大块传输
通道 几乎不参与 最高 大型机专用 I/O

Q2:为什么要有 I/O 接口?

直接连 CPU 总线会遇到四个不匹配:

  • 速度:CPU GHz vs 外设 kHz-Hz——缓冲暂存
  • 格式:串行(USB)vs 并行(CPU 总线)——串并转换
  • 电平:CPU 1.2V vs 外设 5V/12V——电平转换
  • 时序:同步总线 vs 异步外设——握手协议

Q3:中断处理流程

请求→响应(保存 PC/PSW 入栈)→查中断向量表(IDT,x86 有 256 个中断向量)→执行 ISR(关中断或可嵌套)→恢复现场并返回(IRET 指令恢复 PC/PSW)

Q4:DMA 和 CPU 同时访问内存怎么协调?

通过总线仲裁:DMA 控制器在 CPU 不访问内存时"偷"用总线(Cycle Stealing)。更现代的方案是 DMA 通过独立的内存通道(如 IOMMU),不和 CPU 抢总线。


# 第 06 篇·计算机总线系统设计

# 基础思考题

Q1:总线仲裁三种方式

方式 原理 优缺点
菊花链 设备串联,优先级=物理位置 简单但前级故障影响后级
集中仲裁 独立仲裁器统一分配 公平但单点瓶颈
分布式自举 各设备有独立请求线+仲裁逻辑 灵活但布线复杂

PCIe 用的是集中仲裁 + 交换机转发——Root Complex 作为仲裁者,Switch 做包交换。

Q2:PCIe 的 lane 多路复用

PCIe lane 是一对差分信号线(TX+/TX-,RX+/RX-)。x1=1 lane(~1GB/s),x16=16 lane 并行(~16GB/s per direction)。Lane 数量独立决定带宽,不共享时钟。这就是为什么显卡用 x16 而网卡用 x1。

Q3:QPI/UPI vs DMI

QPI/UPI DMI
连接 CPU-to-CPU CPU-to-PCH(南桥)
带宽 ~10.4GT/s ~4GT/s
延迟 极低(~40ns) 较高
作用 多路服务器互连 连接外围低速设备

# 第 07 篇·计算机指令编程原理

# 基础思考题

Q1:寻址方式举例

mov eax, 42          ; 立即寻址(值在指令里)
mov eax, [0x600000]  ; 直接寻址(地址在指令里)
mov eax, [ebx]       ; 寄存器间接寻址(地址在寄存器里)
mov eax, [ebx+4]     ; 基址寻址(基地址+偏移)
mov eax, [ebx+esi*4] ; 变址寻址(数组访问)

Q2:编译四阶段

gcc hello.c -o hello:

  1. 预处理:cpp 展开 #include、#define→.i
  2. 编译:cc1 生成汇编→.s
  3. 汇编:as 生成目标文件→.o
  4. 链接:ld 链接库函数→可执行文件

Q3:定长 vs 变长指令

RISC(ARM 32位) CISC(x86 1-15字节)
解码 极简(固定格式) 复杂(先判断长度)
代码密度 低 高
流水线 简单 复杂
取指 一次一个 先取 16B 再找边界

# 第 08 篇·计算机程序如何执行

# 基础思考题

Q1:五种数据冒险及 Forwarding 解决

add r1, r2, r3    ; r1 = r2 + r3
sub r4, r1, r5    ; r4 = r1 - r5  ← 读 r1,但上条还在 EX 阶段

无 Forwarding:等 add 写回后 sub 再取(浪费 3 个周期)。 有 Forwarding:add 的 ALU 输出直接旁路到 sub 的 ALU 输入——节省 3 个周期。

Q2:2 位饱和计数器

状态机:强不跳(00) ↔ 弱不跳(01) ↔ 弱跳(10) ↔ 强跳(11)。预测错一次不变——需要连续两次错才翻转。循环中的分支(如 for 的结束判断)会在最后两次"不跳→跳→不跳"中保持高准确率。

Q3:乱序执行 vs 顺序提交

乱序执行让指令以"数据就绪"顺序执行,减少流水线停顿。但必须顺序提交(ROB 确保按程序序更新寄存器/内存)——否则中断发生时 CPU 状态不完整,无法恢复。ROB 同时承担了"暂存结果"和"保证精确异常"两个职责。


# 第 09 篇·计算机内存设计原理

# 基础思考题

Q1:虚拟内存解决什么问题?

问题 解决方案
地址冲突(两进程用同一地址) 各自独立虚拟地址空间,映射到不同物理页
内存不够用 不常用页 swap 到磁盘,用到时换回
内存碎片 分页将内存切成固定 4KB 块,无外部碎片
非法访问 页表项权限位(R/W/U/S)MMU 检查

Q2:分段 vs 分页

分段 分页
单位 可变大小段 固定 4KB 页
外部碎片 有(严重) 无
内部碎片 无 少量(最后一页)
共享 困难 容易(页级别共享)

Q3:缺页异常处理流程

  1. CPU 访问虚拟地址,TLB Miss → 查页表 → 页不在内存(Present=0)
  2. CPU 触发 #PF(Page Fault)异常
  3. OS 异常处理程序:检查访问合法性 → 选一个物理页(可能需要换出旧页)→ 从磁盘读入 → 更新页表项(Present=1)
  4. 返回重新执行触发指令——这次页在内存,成功

Q4:TLB 的核心作用

TLB 是页表的 Cache,缓存最近使用的虚拟→物理地址映射。一次 TLB Miss 要走完四级页表(4 次内存访问 ~400ns),而 TLB 命中只需 ~1ns。TLB 命中率从 99% 降到 80%,程序性能下降 ~9%。


# 第 10 篇·计算机二进制和字节

# 基础思考题

Q1:0.1 + 0.2 为什么不是 0.3?

0.1(十进制)= 0.0001100110011...(二进制,无限循环)。IEEE 754 单精度 - 23 位尾数截断,丢失精度。累加时误差一步步放大。本质是有限的二进制位无法精确表示无限的十进制小数。

Q2:补码为什么让减法变成加法?

补码定义:-X = 2^n - X(模 2^n 运算)。A - B = A + (2^n - B) = A + (-B 的补码)——CPU 用同一个加法器电路同时支持加法和减法。一套电路,两套语义。

Q3:UTF-8 编码规则

Unicode 范围 UTF-8 字节数 格式
U+0000-U+007F(ASCII) 1 0xxxxxxx
U+0080-U+07FF 2 110xxxxx 10xxxxxx
U+0800-U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
U+10000-U+10FFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

"汉"(U+6C49)→ 0110 1100 0100 1001 → 11100110 10110001 10001001 → E6 B1 89

Q4:大端 vs 小端——什么时候会踩坑?

  • 网络通信:IP/TCP 头用网络字节序(大端),主机可能是小端→htonl/ntohl 转换
  • 跨平台二进制文件:x86 写的文件 ARM 上读→字节序反转
  • union 取低字节:小端下 union { int i; char c; } 的 c 是 i 的最低字节

# 第 11 篇·计算机异常处理机制

# 基础思考题

Q1:四种异常的区别

类型 触发源 同步/异步 可恢复? 返回位置 例子
中断 I/O 设备 异步 ✅ 下一条指令 键盘按键、网卡收包
陷阱 int 0x80 同步 ✅ 下一条指令 系统调用
故障 指令错误 同步 可能 当前指令(重执行) 缺页异常、除零
终止 硬件故障 同步 ❌ 不返回 双故障、机器检查

Q2:缺页异常全流程

同 §09 Q3。

Q3:Stack Canary 原理

函数入口时在栈上返回地址旁边放一个随机值(Canary),函数返回前检查这个值是否被改变。如果被改变说明发生了栈溢出(覆盖了返回地址),立即终止进程。利用异常机制实现安全防护。

Q4:信号的默认处理

信号 默认动作
SIGINT(Ctrl+C) 终止进程
SIGSEGV 终止 + core dump
SIGKILL 杀死(不可捕获)
SIGCHLD 忽略

# 第 12 篇·计算机 I/O 操作和原理

# 基础思考题

Q1:select / poll / epoll 区别

select poll epoll
fd 数量 1024 硬限制 无限制 无限制
内核数据结构 3 个 bitmap 链表 红黑树+就绪链表
获取就绪 fd O(n) 遍历 O(n) 遍历 O(1) 从就绪链表取
每次调用传入 全部 fd 集合 全部 fd 只传 epoll fd
内核修改 大量复制 大量复制 事件触发

Q2:零拷贝 vs 传统拷贝的数据流

传统 read+write:磁盘→内核缓冲区→用户缓冲区→内核 Socket 缓冲区→网卡(4 次拷贝 + 4 次上下文切换) sendfile:磁盘→内核缓冲区→网卡(2 次 DMA + 2 次上下文切换,0 次 CPU 拷贝)

Q3:阻塞/非阻塞 vs 同步/异步的区别

同步 异步
阻塞 BIO:read() 到数据才返回 —
非阻塞 NIO epoll:内核通知可读,应用自己 read AIO io_uring:内核读完数据直接给到缓冲区

根本区别:同步=应用主动取数据,异步=内核送数据上门。

Q4:Reactor vs Proactor

Reactor Proactor
谁负责读数据 应用自己 read() 内核读完通知"数据在 X"
代表技术 epoll + NIO io_uring / IOCP
回调时机 "fd 可读了" "数据已在缓冲区"
CPU 开销 应用做拷贝 内核做拷贝

# 自测:全书掌握度检查

读完 12 篇答案后,自己测试:

  1. 你写的 a = b + c 从 C 代码到屏幕上出结果,经过了哪些部件?每一步耗时量级?
  2. 为什么 LinkedList 遍历比 ArrayList 慢几倍?与哪两个硬件特性有关?
  3. 多线程下 count++ 有哪三种写法(synchronized/AtomicLong/LongAdder)?为什么 LongAdder 最快?
  4. 0.1 + 0.2 为什么不是 0.3?金融计算该用什么?
  5. 你服务的 QPS 卡在 3000,排查顺序是什么?

答案在本总结文件的 §13(贯通三句话)到 §16(自测清单)中。答不出任一题,回到对应篇章的速通卡重读。

上次更新: 2026/07/12, 19:39:51
13.计算机组成原理总结
README

← 13.计算机组成原理总结 README→

最近更新
01
11.数据库的原理总结
07-12
02
11.操作系统原理总结
07-10
03
资深程序员软能力
07-10
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式