计算机系统复习:从抽象边界找出易错点
本页目录
复习计算机系统,不必重新背一遍所有名词。更有用的办法,是沿着一个程序的运行过程,检查每次跨越抽象层时发生了什么。
本文合并整理自《CSAPP 知识点复习》和《相关教材内容收集》。现有读薄、读厚系列已经覆盖主要知识,这里只补充复习框架与容易混淆的边界,不再复制实验解答。
先画出从问题到执行的路径
一个问题被表述为算法,算法写成程序,程序转换为指令,指令在具体微结构上执行,最终依靠电路和器件完成。每一层都隐藏了部分细节,也把部分约束传给上一层。
例如,算法复杂度没有改变,访问内存的顺序却可能改变实际时间;源代码中看似相同的表达式,在不同类型和优化条件下也可能产生不同指令。理解抽象的价值,是知道何时可以忽略细节,何时必须回到下一层检查。
fork 复制了什么,又共享了什么
普通全局变量和栈变量在父子进程中属于各自的地址空间。子进程对普通变量加一,不会让父进程的变量也加一。Linux 通常用写时复制实现这一语义,不能把实现上的物理页共享误解成变量共享。
文件则需要分层理解:子进程获得自己的文件描述符表,但继承的描述符会引用相同的打开文件描述,因此可能共享文件偏移。独立的描述符表和共享的打开文件状态并不矛盾。详见 fork(2)。
分析输出顺序时,还要说明标准 I/O 缓冲。printf 的用户空间缓冲与内核文件状态不是一层东西;没有刷新的缓冲可能随进程复制。题目如果假定每次输出立即刷新,这就是重要条件。
exec 替换程序,wait 获取状态
成功的 exec 替换当前程序映像,不会像普通函数那样返回,也不会因此创建新 PID。失败时才返回错误。见 execve(2)。
父进程用 wait 系列调用等待子进程状态变化并获取信息;对于已经退出的子进程,回收会释放保留的退出记录。等待还可以涉及停止、继续等状态,不能把所有 wait 行为都简单归结为“释放内存”。进程和信号基础见Linux 概念指南。
源代码变量不等于某个固定 ELF 段
“已初始化变量都在 .data”过于粗略。静态存储期、初始化值、只读属性和编译优化都会影响表示;局部自动变量还涉及栈、寄存器或被优化消除的情况。ELF 是文件格式,也不能直接等同于运行时完整地址空间。
复习链接时,应区分声明、定义、符号解析和重定位。某个名字编译时可见,不代表链接器一定能找到实现;能成功链接,也不代表运行时动态库一定能被正确加载。可以接着阅读编译与版本控制专题。
优化结论必须附带工作负载
增大缓存块可能利用空间局部性,也可能减少可容纳的块数、增加冲突和搬运成本。合并空闲块可能缓解外部碎片,也不意味着所有分配序列下空间利用率都更高。
看到“越大越快”“合并一定更好”这类结论时,先问数据访问模式、缓存容量、分配策略和评价指标是什么。用两个相反的工作负载检验,比记住一句优化口号更有效。
位运算也要区分数学整数、语言规则和机器指令。C 中移位量为负或不小于提升后的左操作数位宽,会触及未定义行为;不能用某台机器碰巧得到的结果代替语言保证。
用最小反例检查理解
每复习一个概念,可以做三件事:解释正常情况,改变一个条件,预测结果会如何变化。例如去掉一次等待、让两个描述符引用同一个文件、改变矩阵遍历顺序。
实验结果与预测不同的时候,先检查遗漏的假设,再回到对应层次。本站读薄 CSAPP提供主线,缓存实验与内存分配实验提供实践入口。旧实验仍需配合其课程文件与运行环境使用。
相关文章
- 【读厚 CSAPP】VII Proxy Lab2016-05-04
- 【读厚 CSAPP】VI Malloc Lab2016-05-03
- 【读厚 CSAPP】V Shell Lab2016-05-02
- 【读厚 CSAPP】IV Cache Lab2016-05-01