计算机系统基础 总复习 🔗
- 计算机系统概述
- 数据的机器级表示和处理
- 程序的转换和机器级表示
- 程序的链接
- 程序的执行
- I/O操作的实现
- 异常和中断*
总复习 🔗
第一章 🔗
- Turbo Debug 的例子
- 三个段:DS、SS、CS
- 通用寄存器:EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP
- 指令指针寄存器:EIP
第三章 🔗
- 操作数:常数、寄存器、内存
- 寻址方式:常数、寄存器、直接、间接、变址、基址加变址
- ESP、EBP用途
- 内存示意图:画格子,写编号,填内容,标变量。常量不要填,低位放低端,高位放高端
- 读图:先定位,再取n个字节
- 指令分类:传送、运算、位操作、跳转
- 两个操作数不能同时为内存
- 指令标后缀,或操作数中有类型明确的,或两个操作数类型一致
- push和pop:push先减再传送,pop先传送再加。
- 赋值操作:长度一样直接传送,长度减少截断,长度增加看源变量类型选择零扩展或符号扩展
- 乘法 AL * OP -> AX 或 AX * OP -> DX-AX
- 移位指令:全部送入CF。SHL/SHR 逻辑移位;SAL/SAR 算数移位(SAL特色溢出,SAR特色补符号);ROL/ROR 循环移位;RCL/RCR 带CF循环移位
- 分支和循环:CF、ZF、SF、OF各有两种(jz有别名je)。ja/jb 无符号整数高于/低于;jg/jl 带符号整数大于/小于:每组加前缀后缀形成八个助记符,四条指令。
- loop指令:若CX为0,跳出循环。若CX不为0,CX–并跳转到label。
- 子程序调用:CALL/LEAVE/RET
- proc和endp伪指令:定义过程
- 堆栈法传递参数
- 保护现场和恢复现场
- 堆栈内分配局部变量
优化 🔗
编译优化的策略有哪些?请说明各种优化策略能提高程序运行速度的原理。(至少5种不同策略)
要点:
- 不仅要说策略是什么,而且要说出背后的原理;
- 出题的本意是想问问,利用哪些硬件特性来提速,即优化后能更好地发挥硬件的作用,加快运行速度。计算机系统是计算机软件和硬件组成的整体。单纯地从软件层面介绍优化(如去掉没有用的废代码减少了执行指令的数目;算出可以计算的表达式的值而不产生相应的机器指令等等),这就与硬件特性无关了。
参考答案:
- 循环展开:将程序执行流程变成一个顺序结构。消除引起循环的跳转指令,使指令流水线利用更充分,避免在指令流水线上产生要被丢弃的“半成品”而浪费时间。
- 有分支语句向无分支语句转换:使用条件传送 cmov* 等指令,可以提高指令流水线的利用率,原因同(1) [可参见 “L03_Intel 中央处理器.pdf : 流水线的控制分支冒险”]。
- 调整指令执行顺序:后面的指令用到前面指令的结果,前面的指令结果还未产生,后面的指令就要等待,产生阻塞就会影响指令流水线的速度。调整指令顺序的目的是减少可能的阻塞 [可参见 “L03_Intel 中央处理器.pdf : 流水线的数据冒险”]。
- 使用执行速度更快的机器指令:例如,将一个变量中的内容乘 2,可以用变量自己与自己相加,也可以用左移运算。不同指令的执行速度不同,使用速度更快的指令代替完成相同功能的慢速指令,会提高速度。注意:此处指的是一条指令被另一条指令所代替;还不是用多条机器指令来代替一条慢速指令的意思,这种一对多的优化,有一点算法优化的味道。
- 使用串操作指令代替用循环一个数据一个数据的处理(传送、比较、串置初值等等):串操作指令产生的根源就是加快速度。
- 使用SIMD:一条指令成组操作,节约了操作次数。
- 使用位数更长的寄存器:使用字节数更大的寄存器,一次就可以处理更多的内容,充分利用硬件中已有数据线宽度;
- 对一个二维数组调整数据处理顺序(如按列序操作调整为按行序操作):提高CPU 中cache 的命中率,减少cache与内存之间来回的数据交换,从而节约时间。
- 变量与寄存器绑定:访问变量变成访问绑定的对应寄存器,访问寄存器的速度要快于访问内存(包括cache)的速度,(无需地址计算,虚地址向物理地址转换等等操作,因而要快)。
- 并行优化:利用多线程、多核等特性。
为了提高程序的执行速度,在编写C语言程序时,可进行哪些优化(不考虑编译器的优化)? (至少给出 5种优化场景,可举例说明)
要点:
- 不考虑编译器的优化,意思在编译器优化开关关闭时,生成的执行程序运行速度要快。
- 优化算法是会提高速度,但我们这门课主要是介绍基本的C语句的执行过程,算法方面不应考虑;此处的优化是指写程序时应该注意的问题。
- 是使用 C 写程序,而不是使用机器指令编写程序,像一条C语句对应的一段机器指令中可能产生的优化不在考虑之列。
参考答案:
- 优化数据的访问顺序,如在for循环里对于二维数组,按照先行序,再列序访问每个元素。
- 减少重复计算,比如for(int i=0;i<strlen(a);i++)中strlen(a)多次计算。
- 调用封装了串操作指令的函数,如memcpy,memset、memcmp等。
- 变递归程序为迭代程序,函数调用传递参数,断点压栈等多种操作,既慢又有栈溢出的风险。
- 用移位实现乘除法运算,比如x*2变为x«1。
- 调整条件语句中组合条件的子条件顺序。例如 if (A && B),假设 90%的情况下A 会成立,10%的情况下 B成立,就应该写成 if (B && A) , 在 90%的情况下,减少了对条件 A 的判断。
- 封装了SIMD 指令的函数调用
- 多线程的利用
- 当然,写程序时,可以做一些编译器可以优化的工作,如去掉废代码;
- 有一些优化是编译器无法无做到的(也可以说是目前的编译器还没有特别聪明),比如,与指针相关的数据访问。
第四章 🔗
- 外部符号:在模块外定义
- 全局符号:在模块内定义,分为强弱符号
- 本地符号:所有static定义的变量
- 局部变量不在符号表中
节: 🔗
.text
目标代码部分。.rodata
只读数据,如格式串、switch-case的跳转表等。.data
已初始化的全局变量.bss
未初始化的全局变量,在目标文件中不占空间.symtab
符号表。.rel.text
.text节相关的可重定位信息.rel.data
.data节相关的可重定位信息.debug/.line
调试信息。.strtab
字符串表,包含.symtab、.debug中的符号以及节名。以null结尾的字符串序列
- 符号解析
- 重定位:R_386_PC32与R_386_32
第五章 🔗
流水线
第六章 🔗
高速缓存
第七章 🔗
IN、OUT指令
功能:从外设寄存器取信息送入AX或AL。 形式: IN $PORT, %AL, 功能:(PORT)→AL
功能:将AX或AL内容送到外设寄存器中。 形式:OUT %AL, $PORT 功能:(AL)→PORT
注:立即数方式端口地址为8位,端口地址>255,就放到DX中
IO方式
- 无条件传送方式,要求外设与CPU同步
- 查询传送方式,循环查询外设状态(准备好?忙?),条件满足就传送
- 中断传送方式,外设准备好就向CPU发出中断申请,提高CPU效率,可以处理突发事件,提高了计算机工作的灵活性。
- 直接存贮器(DMA)传送方式,高速外存与主存通过总线之间传送
第八章 🔗
- 中断的处理过程:中断请求、中断响应、中断处理、中断返回
- 中断系统:实现中断的软硬件设施,包括8259A、中断源、中断请求(硬件)、中断向量表、中断处理程序(软件)
- 中断源:引起中断的原因,发出中断请求信号
- 中断请求:请求CPU响应的信号
- 中断响应:硬件对中断请求作出响应的过程
- 异常或中断处理程序:处理事件的子程序
- 异常:由CPU内部产生的“内部错误”,分为故障(如除零、溢出、缺页),陷阱(INT指令),中止(严重错误),一般中断类型号在指令中,不受IF的影响
- 中断:由外部设备产生的“外部事件”,包括可屏蔽中断(受IF影响)、不可屏蔽中断
- 实模式下,中断向量表,中断号 * 4,中断向量的物理地址
- 保护模式下,中断描述符表,中断号 * 8
INT指令
- 压栈FLAGS、复位IF、复位TF
- 压栈CS、高16位->CS
- 压栈IP、低16位->IP
IRET指令
- 出栈到IP
- 出栈到CS
- 出栈到FLAGS
中断处理
- 在中断处理子程序中,保护现场(PUSHA等),恢复现场(POPA等)
- 设置中断向量前CLI,后STI
- 中断返回用IRET,否则…