Python字符串格式化利器:深入解析format()方法
一、引言:从"老式"到"新式"的进化Python 早期的 % 格式化继承自 C 语言的 printf,语法繁琐且类型绑定严格: 123name = "Alice"age = 30print("Name: %s, Age: %d" % (name, age)) format() 作为 Python 2.6 引入的"新式"格式化方法,是 f-string 的前身,也是目前功能最全面的格式化方案。 核心观点:虽然 f-string 更简洁,但 format() 在模板分离和动态格式化场景中依然是王者。 二、基础语法:三种参数传递方式2.1 位置参数12345# 默认顺序"{} {}".format("Hello", "World") # 'Hello World'# 指定索引——可以打乱顺序或重复使用"{1} {...
网络编程基础
你给异地的朋友寄过信吗?信封上写收件人地址,邮局按地址一层层转发,对方拆信读内容。计算机网络干的是同一件事,只不过把"信"换成"字节",把"邮局"换成路由器,把"地址"换成 IP。CSAPP 第 11 章讲的就是:程序员怎么用一套统一的接口(socket),让两台机器像读写文件一样交换数据。 本文把客户端-服务器模型、协议分层、TCP/UDP、socket 接口、字节序坑、以及一个最小 Echo 服务器一次讲透。代码能跑的我都跑了,跑不了的(C 版需要 gcc)明确标出来。 一、客户端-服务器模型:一切网络的原点不管上层多花哨,绝大多数网络应用都逃不出这个模型: flowchart LR C[客户端<br/>发起请求 等响应] -->|网络| S[服务器<br/>常驻 被动等待请求] S -->|响应| C 客户端是主动方:在某个时刻启动,向已知地址发起连接,拿到结果就走人(浏览器、SSH 客户端都是)。 服务器是被动方:开机就常驻,绑...
C++ 装饰模式
一、模式定义与核心思想装饰模式(Decorator Pattern)是结构型设计模式的重要成员,核心思想是:通过组合而非继承的方式,动态为对象添加额外职责。它避免了继承体系的臃肿,允许灵活组合多个功能,实现 “即插即用” 的扩展效果。 关键特征: 装饰器与被装饰对象遵循同一接口(或继承同一基类) 装饰器持有被装饰对象的引用,实现功能叠加 支持多层装饰,形成职责链 二、UML 类图结构1234567891011121314151617181920+----------------+| 抽象组件(Component) |+----------------+| + operation() |+----------------+ ↑ |+----------------+ +----------------+| 具体组件(Concrete) | | 装饰器基类(Decorator) |+----------------+ +----------------+| + operation() | | -...
系统级 I/O
一句话本质:Unix 把「一切皆文件」贯彻到极致——磁盘、键盘、屏幕、管道、socket,在进程眼里全是「一串字节」。你拿到手的不是文件对象,而是一个小整数(文件描述符 fd),所有读写都靠这个整数在系统调用层完成。理解 I/O,就是理解「fd 这个把手是怎么挂到内核里那张打开文件表上的」。 1. 钩子:为什么 printf 最终都落到屏幕?写 C 的人几乎都从 printf("hello\n") 起步,但很少有人追问:那串字符凭什么出现在终端上,而不是写进某个文件、或者发到网络?答案藏在两层间接之后——printf 先把字节塞进标准库的缓冲区,缓冲区满了或者遇到换行才调用 write(1, ...);而 1 这个 fd,从进程出生的那一刻起就被内核默认指向了「终端」。 说白了,I/O 不是「操作文件」,是「拿着一个编号,去内核里翻一张表」。这一章就把这张表、这条链、以及几个最容易踩坑的返回值讲透。 2. Unix I/O 模型:万物皆字节流CSAPP 把输入/输出抽象得极简。一个 Linux 文件就是「一个 m 字...
Python魔法揭秘:特殊名称与内置功能的"暗号"对照表
一、引言:揭开"魔法"的面纱当你写下 len(my_list) 或 my_obj + 1 时,Python 是如何知道怎么做的? 答案是一套约定俗成的"暗号"系统——特殊名称(Magic Methods)。它们是 Python 对象与解释器之间的通信协议,以双下划线开头和结尾(因此也叫 Dunder Methods,Double UNDERscore)。 掌握这些暗号,你就能让自定义对象像内置类型一样工作。 二、基础篇:对象的"自我介绍"与"生命周期"2.1 对应关系 特殊名称 对应的内置功能 说明 __init__ 对象初始化 构造函数,创建实例时调用 __str__ str() / print() 面向用户的友好展示 __repr__ repr() / 交互式显示 面向开发者的精确描述 2.2 代码演示12345678910111213141516class Person: def __init__(self, name, age): ...
虚拟内存
一句话本质:虚拟内存是「一个间接层」——它让每个进程都以为自己独占一整块连续、从 0 开始的地址空间,而背后由操作系统 + MMU 悄悄把虚拟地址映射到物理内存的任意角落。这一层间接解决三件事:缓存(当主存用)、内存管理(隔离进程)、内存保护(权限)。 1. 钩子:为什么每个进程都「独享」4 GB?你在自己机器上同时开着浏览器、IDE、微信。它们都在读写「地址 0x400000」。如果地址就是物理内存,三个程序早就把彼此踩烂了。但现实是它们相安无事——因为程序看到的地址(虚拟地址)根本不是内存条上的物理位置,中间隔了一层由硬件+内核维护的页表。 CSAPP 把虚拟内存(VM)讲成三件独立但共用一套机制的事,这是本章和 61C《虚拟内存》最大的视角区别:61C 偏「硬件如何让地址翻译跑得快」,CSAPP 偏「VM 作为编程抽象,它给进程提供了哪三种能力」。 2. 地址空间:一个 N 元素的大数组地址空间就是「可能地址的有序集合」。虚拟地址空间大小为 $N=2^n$($n$ 为地址位数;x86-64 用 48 位有效虚拟地址,即 $2^{48}$)。物理地址空间是内存条...
C++ 工厂方法模式
一、模式简介工厂方法模式(Factory Method Pattern)是创建型设计模式的核心成员,其核心思想是:定义一个创建对象的接口(抽象工厂),但由子类(具体工厂)决定实例化哪个类(具体产品)。通过这种设计,将对象的创建逻辑与使用逻辑彻底分离,让代码更具扩展性和可维护性。 二、核心概念与结构工厂方法模式包含 4 个关键角色,形成清晰的继承体系: 抽象产品(Abstract Product) 定义产品的通用接口,所有具体产品都需实现该接口。例如 “交通工具” 抽象类,包含 “行驶” 纯虚函数。 具体产品(Concrete Product) 抽象产品的实现类,是工厂方法最终创建的对象。例如 “汽车”“自行车” 类,分别实现 “行驶” 方法。 抽象工厂(Abstract Factory) 定义创建产品的接口(工厂方法),返回抽象产品类型。例如 “交通工具工厂” 抽象类,包含 “创建交通工具” 纯虚函数。 具体工厂(Concrete Factory) 抽象工厂的实现类,重写工厂方法,返回具体产品实例。例如 “汽车工厂” 创建汽车对象,“自行车工厂” 创建自行车对象。 三、代码示例1...
存储器层次与 Cache
引子:CPU 跑得飞快,内存却拖了后腿上一篇我们聊了处理器怎么靠流水线把 IPC 怼上去。但有个尴尬的事实:CPU 算得再快,数据要是还在慢吞吞的内存里,它也只能干等。 现代 CPU 一个时钟周期能执行好几条指令,而从主存(DRAM)取一个字,动辄要几百个周期。这个时间差就是「存储器墙」。CSAPP 第 6 章给出的解法是存储器层次结构(memory hierarchy)——用一层层更小更快、也更贵的存储,把慢存储的延迟「藏」起来。而其中最关键的一层,就是 Cache。 本质一句话:Cache 之所以能加速,靠的不是魔法,而是「程序大多在反复访问同一小块数据」这个事实——局部性原理。 本文从存储技术讲起,落到 Cache 的地址划分、映射方式、缺失类型和写策略,把第 6 章的骨架讲透。 1. 为什么要分层:一个金字塔存储器的核心矛盾是:快的东西贵且小,大的东西便宜但慢。于是系统设计者把它们叠成金字塔——越往上越快越贵越小,越往下越慢越便宜越大。 graph TD L0["寄存器<br/><1ns · 几百字节"] ...
Python字符串双雄:repr()的精确与f-string的优雅
一、引言:字符串的两种面孔在 Python 交互式命令行中,同一个对象可以呈现两种截然不同的面貌: 1234567>>> s = "Hello\nWorld">>> print(s)HelloWorld>>> s'Hello\nWorld' print(s) 展示的是面向用户的友好输出——换行符真的换行了。而直接输入 s,展示的是面向开发者的精确描述——换行符被保留为 \n,还带着引号。 为什么 Python 需要两种方式来表示对象?因为它们服务于不同的受众:用户需要可读性,开发者需要精确性。 核心观点:repr() 追求精确与可复现性,f-string 追求可读性与灵活性。 二、repr():对象的"官方身份证"2.1 核心概念repr() 旨在返回一个"官方"的字符串表示。理想情况下,这个字符串应该能作为 Python 代码来重新创建该对象: 1eval(repr(obj)) == obj 这不是硬性要求,但对于内置类型(如 int、str...
C++ 实现简单工厂模式
一、简单工厂简单工厂模式的核心是通过一个工厂类统一创建不同产品实例,本质是将对象创建逻辑与使用逻辑分离。C++ 作为面向对象编程语言,天然支持类与继承、多态、虚函数等特性,相比 C 语言的模拟实现,能更直接、优雅地满足简单工厂模式的设计意图,且无需手动管理函数指针与内存分配的绑定关系。 从两个维度理解适配性: 语法维度:通过抽象基类定义产品接口,具体产品类继承基类并实现纯虚函数,工厂类通过静态成员函数创建产品实例,完全符合面向对象的设计规范 功能维度:利用 C++ 的多态特性,调用者可通过基类指针 / 引用统一操作不同产品,无需关注具体产品类型;通过构造函数与析构函数自动管理内存,避免内存泄漏风险 二、核心结构2.1 核心角色定义(面向对象原生支持) 角色名称 实现方式 核心职责 抽象产品(Product) 抽象基类(含纯虚函数) 定义所有产品的统一接口,规范产品行为 具体产品(ConcreteProduct) 继承抽象基类的子类 实现抽象产品的纯虚函数,提供具体产品的业务逻辑 工厂(Factory) 包含静态成员函数的类 根据输入参数(如类型枚举、字...
处理器架构与流水线
上一章我们看 gcc -Og -S 的输出,一条 C 语句常变成三五行汇编。但如果你以为 CPU 是"执行完一条再取一条",那所有性能直觉都会跑偏:现代处理器在同一时刻手里至少攥着五条指令。 最典型的一幕是这样两行: 12mrmovq 8(%rsp), %rax # 从内存读一个值addq %rax, %rbx # 立刻用它 第二条在"执行"阶段就要用 %rax,可第一条的结果还卡在"访存"阶段。按最朴素的算法,CPU 得干等两拍;实际只停了一拍。这一章要拆的就是这个窟窿是怎么被填上的——顺带回答另一个问题:为什么整个流水线里最贵的指令是 jne。 1. ISA 是合同,微架构是实现先把两个容易混的词分清: ISA(指令集架构):程序员看到的一切——有哪些指令、有哪些寄存器、内存怎么寻址、异常怎么触发。它是硬软件之间的合同。 微架构:这份合同的一种实现。同样的 x86-64 合同,Intel 用乱序超标量实现,AMD 用另一套,结果都能跑同一个二进制。 CSAPP 为了讲清微架构,自己造了...
链接(Linking)
两个 .c 文件,一个定义 int sum(int),另一个 extern 声明它,各自编译都过,最后 gcc main.o sum.o 才变成一个能跑的进程。可如果你把这两个 .o 的顺序换成 gcc sum.o main.o 之外再加个静态库,就可能蹦出一句 undefined reference to 'addvec'——而那个符号明明就在你传给链接器的 .a 里。链接器不是"把一堆字节拼起来",它是一道有状态的扫描过程:先收集符号,再决定谁被留下,最后才把地址填进指令里。 这一篇把 CSAPP 第 7 章讲透:编译系统四步、ELF 目标文件的内部结构、强/弱符号的三条规则、静态库为什么对命令行顺序敏感、重定位那条 PC 相对公式怎么手算、以及动态链接里 GOT/PLT 是怎么做到"第一次慢、以后直接跳"的。 1. 从 hello.c 到 a.out:四步走 预处理器 cpp hello.c → hello.i 编译器 cc1 hello.i → hello.s 汇编器 as hello....
Python深度解析:Yield, Return与Yield From的时空魔法
一、引言:打破"一次性"函数的诅咒普通函数有一个致命特征——一次性。执行到底,return 结果,销毁所有局部变量。人死灯灭,不留痕迹。 但有些场景需要函数"记住"上次执行到哪里了。比如遍历一个大文件,你不想一次性读入内存,而是读一行、处理一行、再读一行。这就需要函数拥有"记忆"——生成器应运而生。 生成器让函数从"单向流水线"变成了"可暂停的状态机"。 二、Yield:时间的暂停与状态的冻结2.1 核心机制当代码执行到 yield 时,函数并没有结束,而是"挂起"了: 保存当前的执行位置和所有局部变量 产出一个值给调用者 交还控制权,等待下次被唤醒 123456789101112131415161718192021def counter(n): i = 0 while i < n: print(f" [生成器内部] 即将 yield {i}") yield i ...
规范化 Git 提交 -- commitlint + husky
导言在团队开发或开源项目协作中,Git 提交信息如同代码的 “说明书”,直接影响代码可维护性与问题追溯效率。然而实际开发中,提交信息往往存在格式混乱、描述模糊等问题,例如 “fix bug”“update code” 这类无意义的表述。本文将通过 commitlint(提交信息验证工具)与 husky(Git 钩子管理工具)的组合,带你实现提交信息规范化与自动化校验,彻底解决这一痛点。 一、提交信息的常见问题与规范需求1.1 典型问题分析在未实施规范的项目中,提交信息通常存在以下问题: 格式混乱:无固定结构,有的包含类型,有的仅描述内容 描述模糊:如 “修改样式”“优化代码”,无法快速理解变更目的 信息不全:未关联需求编号或 Bug ID,问题追溯困难 语义缺失:无法通过提交信息判断变更类型(如功能新增、Bug 修复、文档更新) 1.2 规范标准选择:Conventional Commits目前行业广泛采用的 Conventional Commits(约定式提交) 标准,定义了结构化的提交信息格式: 12345<type>[optional scope]:...
缓冲区溢出与安全
一个"读一行输入再打印出来"的小程序,塞进去 80 个字母就崩了;同一个二进制,换台机器跑同样的输入却没事;某些崩溃信息里赫然写着 *** stack smashing detected ***,而另一些则静悄悄地把控制权交给了别人。这三件事背后是同一个机制:C 语言不为数组访问做任何边界检查,而函数的返回地址就躺在局部数组的高地址侧 —— 于是"写越界"这件事,在物理上等同于"改写程序接下来要执行哪条指令"。 这一篇把 CSAPP 3.10 那套攻防逻辑讲透:溢出是怎么发生的、攻击者如何从"改一个地址"升级到"执行自己的代码"、现代编译器和操作系统架了哪三道防线、以及这三道防线各自的缝在哪。 1. 先看清栈帧:返回地址就在缓冲区头顶函数调用时,x86-64 在栈上给被调函数划一块帧。以 echo() 里有一个 char buf[64] 为例,帧内从高地址到低地址依次是: echo() 的栈帧:返回地址就压在 buf 的头顶 高地址 0x7fffffffe1a8 返回地址(...
机器级程序:汇编与栈帧
递归层数稍多一点程序就 Segmentation fault;两个看起来一样的循环,改了一行判断顺序性能差一倍;多线程里 i++ 累加结果永远小于预期。这三件事在 C 源码层面都"看起来没问题"——因为决定它们的是编译器生成的那条机器指令序列,而不是你写的那行 C。CSAPP 第 3 章干的事,就是把 C 和机器之间那层黑盒掀开:读得懂汇编,你才有资格谈"这段代码快不快"。 1. 从 C 到可执行文件:四步流水线一段 main.c 变成能跑的 a.out,中间经历四道工序,每一步产物都能落在磁盘上: 12345gcc -E main.c -o main.i # 1 预处理:展开 #include / #definegcc -Og -S main.i -o main.s # 2 编译:C -> 汇编文本(人能读的最后一层)gcc -c main.s -o main.o # 3 汇编:汇编 -> 可重定位机器码gcc main.o -o a.out # 4 链接:多个 .o + 库 ->...
Python继承机制与C++的核心区别:权限控制视角
一、核心差异总结一句话概括:C++ 拥有编译期强制访问控制,Python 只有运行时命名约定。 C++ 的 public/protected/private 是编译器强制执行的——违规代码根本无法编译。Python 的 _/__ 前缀只是"君子协定"——技术上你总能绕过去,Python 相信"我们都是成年人"。 二、Python 的三种"伪权限"2.1 Public(var):普通继承行为没有任何前缀的属性就是公开的,子类和外部都可以自由访问: 1234567891011121314class Animal: def __init__(self, name): self.name = name # 公开属性 def speak(self): # 公开方法 return f"{self.name} makes a sound"class Dog(Animal): d...
团队 Git 协作规范整理
一、分支管理:搭建 “分工明确” 的协作骨架混乱的分支体系是团队 Git 协作的万恶之源。想象一下:有人在main分支直接写代码,有人用 “test1”“newcode” 命名分支,合并时根本分不清分支用途 —— 这种场景下,冲突和版本混乱只是时间问题。 1. 推荐:简化版 Git Flow 分支结构企业级项目中,无需过度复杂的分支模型,一套 “主分支 + 辅助分支” 的简化结构足以满足需求,核心是明确每个分支的 “生命周期” 和 “职责边界”: 分支类型 命名规范 核心用途 操作红线 主分支 main/trunk 存放生产环境代码,始终保持 “可部署” 状态(任何时候拉取都能正常运行) 严禁直接push,仅通过 PR 合并,合并前必须经过测试 开发分支 develop 团队日常开发集成分支,汇总各功能分支代码,是预发布前的 “代码蓄水池” 不直接在该分支写代码,仅接受功能分支合并 功能分支 feature/模块名-需求描述 单个功能 / 需求的独立开发分支(如feature/user-login) 从develop创建...
信息表示:位、字节、整数与浮点
0.1 + 0.2 在 C/Java 里都不等于 0.3;300 强转成 unsigned char 会变成 44;网络里收到的 0x12345678 在你本机读出来是 0x78563412。这三个"离谱"现象,根子都在同一个问题上:计算机怎么用 0/1 表示一个数。CSAPP 整门课都在讲"程序员的计算机系统视角",第一篇就从地基开始——信息表示。 1. 位、字节与字长计算机只能存两种状态,记作 0 和 1,一位就叫一个 bit(位)。8 个 bit 组成 1 字节(byte),这是内存编址的最小单位。所谓"机器字长",指 CPU 一次能处理的位数——64 位机器上指针就是 8 字节。写一段代码把"地基尺寸"打出来: 1234567891011#include <stdio.h>int main(void) { printf("sizeof(char) = %zu 字节\n", sizeof(char)); printf(...
性能与 Amdahl 定律
1. 双核变四核,为什么没快 4 倍老板说"加机器就能快",于是核心数从 1 涨到 4、到 64。可你一测:1 核跑 100 秒的任务,4 核跑了 30 秒——不是 25 秒;64 核竟然还有 20 秒。钱花出去了,倍数却越来越"不划算"。 这不是编译器偷懒,而是一条冷冰冰的物理/数学上限在起作用——Amdahl 定律。它回答的是:一个程序里只有一部分能并行,整体到底最多能快多少? 本质一句话:程序里跑不并行的那一块,决定了它再怎么加核也快不到哪去。 2. 加速比:先定义"快了多少"记: 原串行总耗时 T₁(1 个核心跑完)。 并行化比例 P(0~1):原本可以并行执行的那部分时间占比。 串行比例 1 − P:怎么都并行不了的硬骨头(初始化、I/O、临界区、依赖链)。 用 N 个核心跑,并行部分耗时按 N 等分缩成 P·T₁ / N,串行部分纹丝不动,仍是 (1 − P)·T₁。 于是 N 核总耗时: 12T(N) = (1 − P)·T₁ + P·T₁ / N = T₁ · [ (1...
Python进阶必修课:掌握Zip, Map, Filter, Reversed的优雅之道
一、引言:告别冗长的 For 循环你一定写过这样的代码: 123456names = ["Alice", "Bob", "Charlie"]ages = [25, 30, 35]result = []for i in range(len(names)): if ages[i] > 28: result.append(names[i].upper()) 索引操作、条件判断、手动追加……这段代码能跑,但不够 Pythonic。Python 提供了四个核心内置函数——zip、map、filter、reversed,它们让数据处理像搭积木一样简洁。 更重要的是,它们返回的都是迭代器,采用惰性求值(Lazy Evaluation)——数据不是一次性全部生成,而是按需产出。这意味着处理百万级数据时,内存占用可能只有几个字节。 二、Map:数据转换的流水线2.1 核心作用对序列中的每个元素执行相同的操作(映射)。 1map(function, iterable) 2.2 实战场景批量类型转换: 123st...
类图设计--编程的前置准备
一、类图设计方法论:构建稳健的面向对象模型类图建模的本质是将现实世界的业务概念转化为计算机可理解的面向对象结构。遵循科学的方法论是确保模型质量的基础,核心包含四大环节:元素识别、关系构建、属性定义与模型优化。 1.1 元素识别:精准定位核心建模单元元素识别是类图设计的起点,需从业务需求中提取关键概念并转化为 UML 元素。识别过程需遵循 "单一职责原则",确保每个元素职责清晰、边界明确。 核心元素类型及识别方法 元素类型 识别特征 表示符号 应用场景 类 (Class) 具有相同属性和行为的对象集合 矩形(分三层:类名 / 属性 / 方法) 业务实体(如 User、Order)、控制逻辑(如 OrderService)、工具组件(如 DateUtils) 接口 (Interface) 定义行为契约,无具体实现 棒棒糖形状或矩形(标注 <>) 服务契约(如 PaymentGateway)、模块边界(如 UserRepository) 抽象类 (Abstract Class) 不能实例化,包含抽象方法 类名斜体或标...
硬件、软件接口
1. 一行 C 代码,凭什么能点亮一盏灯嵌入式里最让人"上头"的一行代码,大概是这样的: 1*((volatile unsigned *)0x10012000) = 1; // 把 GPIO 某个引脚置 1,灯就亮了 从语法看,它无非是"往一个地址写 1"。可那个地址上既没变量、也没内存,而是一颗 LED 的引脚寄存器。为什么一行赋值就能让物理世界的灯亮? 答案藏在一个核心事实里:软件和硬件之间,从来不是靠"魔法"对接,而是靠一份早就约定好的地址与规则——这份契约,就是本文的主题"硬件 / 软件接口"。 本质一句话:软件能指挥硬件,是因为有人把"硬件的开关"映射成了软件能访问的"地址",并规定了访问它的规矩。 2. ISA:软硬件之间的第一份契约最底层的接口是指令集架构(ISA,Instruction Set Architecture)。它是 CPU 设计者和编译器作者之间的一份"互不入侵"的契约: 对软件(编译器 ...
基于图形面积计算项目的 OOA/OOD/OOP 全流程解析与类图设计
一、面向对象分析(OOA):需求提取与实体识别1.1 核心业务需求本系统旨在实现以下关键功能: 支持对多种基础几何图形,包括矩形、圆形和三角形的面积计算; 以统一的方式展示不同类型图形的名称及其对应的面积计算结果; 构建具有高度扩展性的系统架构,确保在新增图形类型时,现有计算逻辑无需进行任何修改。 1.2 核心实体识别(3 个以上核心实体)经过严谨的需求分析,本研究确定了以下核心业务实体,并对各实体的属性、行为及业务约束进行了详细定义: 实体名称 核心属性 核心行为 业务约束 图形(Figure) 无直接属性(抽象概念) 获取名称、计算面积 作为抽象基类,不可实例化,需由具体图形类继承 矩形(Rectangle) 长度(length)、宽度(width) 计算面积、返回名称 长度和宽度必须为正数 圆形(Circle) 半径(radius) 计算面积、返回名称 半径必须为正数 三角形(Triangle) 三边长度(a、b、c) 计算面积、返回名称 三边长度需满足三角不等式(a+b>c 等) 图形管理器(FigureManager) 图形集...
中断与 I/O
1. 为什么 CPU 不能"干等"外设设想一个场景:CPU 想从磁盘读一个扇区。磁盘是机械部件——磁头要移动、盘片要旋转,一次读取就是几毫秒。而 CPU 的时钟周期以纳秒计,几毫秒相当于上百万个周期。 如果 CPU 发出读命令后就在原地等,那等于一台 3 GHz 的机器,为了等一个慢六个数量级的外设,把上百万个周期白白烧掉。这就是"CPU 与外设的速度鸿沟"——也是本章所有 I/O 机制的出发点。 本质一句话:I/O 的全部学问,就是想办法让慢速外设别把快速 CPU 拖死。 2. I/O 的三种基本方式处理器与设备交换数据,历史上演化出三种方式: 方式 等待期间 CPU 在干嘛 每块数据代价 适用场景 轮询(polling) 空转查状态位 等满整个设备延迟 极简单、极低速设备 中断(interrupt) 干自己的正事,就绪才被"打断" 一次上下文切换 中低速、事件驱动设备 DMA 完全不管,数据由控制器搬 只有开始/结束各一次中断 大块连续数据(磁盘、网卡、GP...

