csapp学习

第一章 计算机系统漫游

1、大部分现代计算机系统都使用ASCII 标准来表示文本字符

2、八位一字节

3、\n的ascii码为10,6进制0x0a

4、像hello.c这样只有ACSii字符构成的称为文本文件,所有其他文件都被称为二进制文件

5、程序被编译的过程[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s5wuxfyE-1617068293520)(C:\Users\40704\AppData\Roaming\Typora\typora-user-images\image-20210302170727520.png)]

  • 预处理阶段:预处理器cpp读取系统的头文件的内容,并把它直接插入到系统文本中。得到.i 文件
  • 编译阶段
  • 汇编阶段
  • 链接阶段

6、P4、5页的问题的答案

7、处理器的核心是大小为一个字的存储设备,称为程序技术器(PC)。在任何时刻,pc都指向主存中的某条机器语言指令

8、pc的位数?

9、操作系统实现一个cpu并发执行多个程序的机制为:上下文切换。

10、Amdahl定律——加速比S

11、操作系统内核是应用程序和硬件之间的媒介,他提供三个基本的抽象:1)文件是对i/o设备的抽象,2)虚拟内存是对主存和磁盘的抽象,3)进程是处理器,主存和i/o设备的抽象。

第二章 信息的表示和处理

  1. 无符号编码基于传统的二进制表示法
  2. 补码编码是表示有符号整数的最常见的方式,有符号整数就是可以为正或者为负的数字。
  3. 浮点数编码是表示实数的科学计数法的以2为基数的版本。
  4. 大多数计算机使用8位的块,或者字节,作为最小的可寻址的内存单位,而不是访问内存中单独的位
  5. 尽管c编译器维护着这个类行信息,但是他生成的实际机器级程序并不包含关于数据类行的信息(??????????)
  6. 0x或0X开头为十六进制
  7. 一个字节的值域0x00~0xFF
  8. 十六进制大小写一样。
  9. A——1010,C——1100,E——1110
  10. [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-shOpKLox-1617068293521)(C:\Users\40704\AppData\Roaming\Typora\typora-user-images\image-20210310155229264.png)]
  11. char *在32位和64位的字节数,float的字节数,long的字节数
  12. unsigned 关键字表示是否为有符号变量,char默认为什么根据编译器。
  13. 数据在地址中的存储顺序:大端法——顺着,高位在低地址。小端法——低位在低地址。
  14. 理解P32的不同数据在不同机器下跑出的结果。
  15. 字符串的结尾默认为\n,strlen()函数计算的长度不包括\n
  16. unicode,utf-8,ascii的概念
  17. a^a=0,利用异或运算可以实现翻转
  18. 注意掩码的技巧:x&0xFF,(x^0xFF),x^(0xFF)
  19. 逻辑运算符如果第一个参数求职就能确定表达式结果,那么逻辑运算符就不会对第二个参数求职
  20. 移位运算:逻辑左移,逻辑右移,算术右移。算术右移对有符号整数数据的运算有帮助。
  21. 几乎所有编译器都对有符号数使用算术右移。
  22. 二进制用后缀字母B,十六进制用H
  23. 进制的小数转换
  24. 补码编码的运算,注意公式
  25. 截断和拓展的变换
  26. 注意有符号整数的最小数的表示,注意范围
  27. 判断是大端机器还是小端机器的方法。
  28. 对于常数的乘法, C 编译器自动生成移位和加法代
  29. 无符号整数除法:逻辑右移,有符号:算术右移
  30. 由于整数乘法比移位和加法的代价要大的多,许多C语言编译器试图以移位、加法和减法的组合来消除很多整数乘以常数的情况。
  31. 除以二的补码除法,注意使用偏置值

重要例题:2.10 2.11 2.12 2.13 2.19 2.21 2.24

第三章 程序的机器级表示

处理器的发展历程


程序编码过程

  1. C预处理器(cpp)拓展源代码,插入所有用#include命令指定的文件,并拓展所有用#define声明指定的宏.生成.i中间文件。
  2. 编译器(cc1)产生俩个源文件的汇编代码(.s文件)
  3. 汇编器(as)将汇编代码转化成二进制目标代码文件。
  4. 链接器将俩个目标代码文件与实现库函数的代码合并,并产生最终的可执行代码文件。

目标代码是机器代码的一种形式,它包含所有的二进制表示,但还没有填入全局变量的地址。

可执行代码是机器代码的第二种形式,也就是处理器执行的代码格式。


机器级编程的抽象

  1. 第一种是有ISA来定义机器级程序的格式和行为,它定义了处理器状态、指令的格式,以及每条指令对状态的影响。
  2. 第二种抽象是,机器级程序使用的内存地址是虚拟地址,提供的内存模型看上去是一个非常大的字节数组

x86-64含有的寄存器

  1. 程序计数器——PC,%rip
  2. 整数寄存器
  3. 条件码寄存器
  4. 一组向量寄存器

程序的内存包含:程序的可执行代码,操作系统需要的一些信息,用来管理过程调用和返回时栈,以及用户分配的内存块


一些工具

gcc

注意大小写

  • -Og,-O1,-O2:优化级别
  • -o : 设定输出文件,不加的话会默认生成a.out
  • -S:生成汇编文件
  • -c:生成目标代码文件(.o)

objdump

objdump -d mstore.o

汇编代码的格式

AT&T:gcc和objdump的默认格式

intel格式

二者有一定区别


通用目的寄存器

也叫整数寄存器

规则

  1. 生成1字节和2字节的指令会保持剩下的字节不变
  2. 生成4字节的指令会把高位4个字节置为0。

分类

  1. %rax存返回值
  2. %rip存栈指针
  3. %rdi,%rsi,%rdx依次存参数
  4. 其他用于调用者保存和被调用者保存

操作数指示符

操作数分类

  1. 立即数——$+c语言语法的整数;(无0x就是10进制)
  2. 寄存器
  3. 内存引用

寻址模式

比例变址寻址:一个立即数偏移;基址寄存器;变址寄存器;比例因子(只能为1,2,4,8)


常用指令

数据传送指令

movb,movw,movl,movq,movabsq  

常规的movq指令只能以表示32位补码数字的立即数作为源操作数,然后把这个值符号拓展得到64位的值,放到目的位置。movabsq指令可以以任意64位立即数作为源操作数,并且只能以寄存器为目的。

movz,movs

movz为零扩展数据传输指令,movs为符号扩展指令;

没有movzlq指令,通过movl实现;

cltq

cltq把%eax扩展到%rax

//找错
movb $0xF,(%ebx)
movw (%rax),4(%rsp)
movq %rax,$0x123

压入和弹出栈指令

pushq,popq

注意二者的等价形式

算术和逻辑操作

加载有效地址

leaq

一元操作符

inc,

加减乘除异或

addq,subq,xor

移位运算符

sal,sar,shr

移位运算符也有单操作数形式,相当于移动1位.(3.26)

特殊的算术操作

imulq,mulq,cqto,idivq,divq

imulq指令有两种形式,双操作数和单操作数

分为imulp和mulp

单操作数乘法指令用于计算俩个64位值的全128位乘积,要求一个存放在%rax中,另一位为源操作数.

乘积结果高64位存放在%rbx中,低64位在%rax中.(低高位是指字节地址,反应在寄存器就是(%rsi)和8(%rsi))


控制

条件码

cmp*,test*

cmp只设置条件码而不改变任何其他寄存器

cmp与sub指令行为一样

test与and指令一样

典型的test用法:testq %rax,%rax来测试是否为0

访问条件码

跳转

跳转指令的编码

当执行相对寻址时,程序计数器的值是跳转指令后面的那条指令的地址.而不是跳转指令本身的地址.------体现在机器码上

用条件传送来实现条件分支

cmov*

基于条件数据传送的代码会比基于条件控制转移的代码性能要好.(原因:流水线)


循环

do while循环和while循环

while循环

while循环的编译优化级别不同,得到的汇编代码不同.

for循环

switch

switch语句可以根据一个整数索引值进行多重分支.

使用跳转表数据结构.

和使用很长的if-else语句相比,使用跳转表的优点是执行开关语句的时间和开关情况的数量无关.

gcc根据开关情况的数量和开关情况值的稀疏程度来翻译开关语句,当开关情况数量比较多,并且值的范围跨度比较小时,就会使用跳转表.

jmp *.L4(,%rsi,8) //汇编中利用跳转表的语句.   *:间接跳转	比例变址寻址

switch语句中,会先对输入的n根据跳转情况进行一定的调整,然后比较在一定的范围,剩下的根据跳转表跳转,跳转表对重复情况的处理是使用同一代码标号,对缺失的情况使用默认的标号.

.rodata //只读数据
ja 		//无符号数大于

过程

过程P调用过程Q,Q在执行后返回到P。这个过程包括下面一个或多个机制:传递控制,传递数据,分配和释放内存。

当x86-64过程需要的存储空间超出寄存器能够存放的大小时,就会在栈上分配空间,这个部分称为栈帧。

x86-64可以通过及攒机最多传递6个整形参数。

通过栈传递参数时,所有的数据大小都向8的倍数对其。

栈上的局部储存

局部数据必须储存在内存中的情况.

  • 寄存器不足够存放所有的本地数据
  • 对一个局部变量使用&
  • 某些局部变量是数组或结构,因此必须能够通过数组或结构引用被访问到.

->和.的区别

(*rp).width=rp->width

如果定义的结构体是指针,那么使用->.如果不是,则通过.来访问成员变量.


数据对齐

任何K字节的基本对象的地址必须是K的倍数.


内存越界和缓存区溢出

缓存区溢出:在定义数组后,会为其在栈上分配相应的空间,如果后续的写入等操作超过数组大小,会导致其他区域被改写

防护措施

栈随机化

每次使用的栈的位置都发生改变,可以防止恶意攻击的代码找到栈,进行修改.

栈破坏检测

在栈帧中任何局部缓存区与栈状态之间存储一个特殊的金丝雀值.

限制可执行代码区域

限制哪些内存区域能够存放可执行代码.


浮点代码

  1. 过程P调用过程Q,Q在执行后返回到P。这个过程包括下面一个或多个机制:传递控制,传递数据,分配和释放内存。
  2. 当x86-64过程需要的存储空间超出寄存器能够存放的大小时,就会在栈上分配空间,这个部分称为栈帧。
  3. 注意call指令过程中,pc值和栈指针的变化。
  4. x86-64可以通过及攒机最多传递6个整形参数。
  5. 通过栈传递参数时,所有的数据大小都向8的倍数对其。
  6. 16个整数寄存器的分类。
  7. short **U[6]的大小
  8. 定长数组和变长数组在编译器层面的优化
  9. 数据对齐:
  10. &和leaq指令
  11. 3.46的知识点.
  12. 缓存区溢出,栈随机化,金丝雀值(栈保护),限制哪部分内存可以储存可执行代码。
  13. long *p; p++; 的汇编代码是什么? 存放指针的寄存器加多少?

重要例题:3.41;3.42;3.44***;3.46***;

第四章 处理器体系结构

Y86-64

程序员可见状态

程序寄存器,条件码,PC,虚拟内存,状态码

Y86-64指令

  • 指令编码长度从1个字节到10个字节不等
  • Y86-64的算术指令中不能使用立即数,需要将常量先加载到寄存器中
  • .pos伪指令告诉从某某地址开始, .align 8指令告诉按8字节边界处对其

一些指令的详情

pushq %rsp	//将栈指针压入栈

虽然之前章节说这条等价于

pushq %rbp
sub $8,%rsp
movq %rbp,(%rsp)

但是实际存入栈的还是%rsp的原地址,而不是减8后的.

更深层次的原因是在于后面SEQ的顺序中讲到,在译码阶段,%rsp的值已近被保存到valA中,后续的访存阶段也用的是这个值,%rsp因为-8而减小是在执行阶段,影响的是valE的值.

popq %rsp
/**/
movq (%rsp),%rax
add $8,%rsp

RICS和CISC

CISC:复杂指令集计算机

RISC:精简指令集计算机

x86-64属于CISC

ARM属于RICS

HCL

存储器和时钟

时钟寄存器

存储单个位或字.时钟信号控制寄存器加载输入值.

Y86-64用时钟寄存器保存程序计数器(PC),条件码寄存器(CC),程序状态(Stat).

随机访问存储器

包括:a.虚拟内存,b.寄存器文件.(Y86-64有15个程序寄存器)

寄存器文件(又叫寄存器堆)
  • 寄存器文件有俩个读端口,还有一个写端口.(不一定只有一个写端口)
  • 读端口有地址输入srcA和srcB
  • 数据输出valA,valB
  • 写端口有地址输入****dstW,数据输入valW

读过程:

当srcA或srcB被设成某个寄存器ID时**,在一段延迟后**,存储在相应寄存器的值就会出现在valA或valB上.

写过程:

每次时钟上升时,输入valw上的值会被写入输入dstW上的寄存器ID指示的程序寄存器.当dstW设为特殊的ID值0xF时,不会写任何程序寄存器.

SEQ处理阶段

取指(Fetch)

  • 根据PC值提取第一个字节,分为俩个4位的数.分别被icode,ifun控制逻辑块读取.
  • 根据icode值,可以得到三个一位的信号,instr_valid(是否合法的指令),need_regids(指令是否包括寄存器指示符字节),need_vals(指令是否包括常数字)
  • imem_error信号.
  • Align硬件单元会处理剩下的9个字节,将他们放入寄存器字段,和常数字.

译码(Decode)

对指令字段译码,产生寄存器文件使用的四个寄存器表示符----dstE,dstM,srcA,srcB

//HLC for srcA in SEQ
word srcA = [
	icode in {IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ}: rA; 			#注意IOPQ和IPUSHQ
	icode in {IPOPQ, IRET}: RRSP;			#注意IRET
	1 : RNONE;
]

执行(execute)

执行阶段包括ALU,ALU功能根据alufun信号,对输入值,进行ADD,SUBTRACT,AND或EXCLUSIVEOR运算.

ALU的输出就是valE信号.

//HLC for ALUA in SEQ
word aluA = [
	icode = {	} : valA;
	icode = {	} : valC;
	icode = {	} : -8;
	icode = {	} : 8;
	#other instructions don't need ALU;   #which?..............待填
]


word alufun =[

]

word set_cc = icode in { IOPQ };

访存(memory)

word mem_addr = [
	
]

bool mem_read = icode in {IMRMOVQ, IPOPQ, IRET };

word Sata = [
	
]


写回(write back)

#待分析
word dstE = [
	icode in {IRRMOYQ, IIRMOVQ, }: rB;		#IMRMOVQ的特殊,之后看............待填irmovq
	icode int {IPOPQ, }
]

更新PC(PC update)

word new_pc = [
	:valC;
	:valC;
	:valM;
	:valP;
]

各种指令的流程跟踪

rrmovqirmovqmrmovqrmmovqOPq
取指icode:funicode:ifun=M1[PC]icode:ifun=M1[PC]icode:ifun=M1[PC]icode:ifun=M1[PC]icode:ifun=M1[PC]
rA:rBrA:rB=M1[PC+1]rA:rB=M1[PC+1]rA:rB=M1[PC+1]rA:rB=M1[PC+1]rA:rB=M1[PC+1]
valCvalC=M8[PC+2]valC=M8[PC+8]valC=M8[PC+8]
valPvalP=PC+2valP=PC+10valP=PC+10valP=PC+10valP=PC+2
译码valA,srcAvalA=R[rA]valA=R[rA]valA=R[rA]
valB,srcBvalB=R[rB]valB=R[rB]valB=R[rB]
执行valE=valB OP valAvalE=0+valAvalE=0+valCvalE=valB+valCvalE=valB+valCvalE=valB OP valC
Cond. Codesset CC
访存Read/write valMvalM=M8[valE]M8[valE]=valA
写回E port, dstER[rB]=valER[rB]=valER[rb]=valE
M port,dstM
更新PCPCPC=valPPC=valPPC=valPPC=valPPC=valP

由于内存可能为变址寻址操作数,mrmovq和rmmovq也需要rB

pushqpopqjxxcallret
取指icode:funicode:ifun=M1[PC]icode:ifun=M1[PC]icode:ifun=M1[PC]icode:ifun=M1[PC]icode:ifun=M1[PC]
rA:rBrA:rB=M1[PC+1]rA:rB=M1[PC+1]rA:rB=M1[PC+1]
valCvalC=M8[PC+1]valC=M8[PC+8]
valPvalP=PC+2valP=PC+2valP=PC+9valP=PC+10
译码valA,srcAvalA=R[rA]valA=R[%rsp]valA=R[rA]
valB,srcBvalB=R[%rsp]valB=R[%rsp]valB=R[rB]
执行valE=valB OP valAvalE=valB+(-8)valE=valB+8valE=valB+valC
Cond. CodesCnd=Cond(CC,fun)
访存Read/write valMM8[valE]=valAvalM=M8[valA]M8[valE]=valA
写回E port, dstER[%rsp]=valER[%rsp]=valE
M port,dstMR[rA]=valM
更新PCPCPC=valPPC=valPPC=Cnd?valC:valPPC=valP
  • pushq要用到valB,注意写回和访存操作?
  • popq为什么要用valA,valB存同样的? valE为什么用valB? 访存写回为什么这样做?
  • jxxq和callq的取指指令
  • jxxq的执行阶段和PC update阶段

流水线

SEQ+

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YbTdQKsx-1617068293522)(C:\Users\40704\AppData\Roaming\Typora\typora-user-images\image-20210329152031963.png)]

pipe-

image-20210328190138552
  • 更新PC提前
  • selectA是在valA和valP之间选择
  • AlUA的值是在valC和valA之间选择

流水线冒险

原因
方法
  1. 用暂停来避免数据冒险
  2. 用转发来避免数据冒险
  3. 加载/使用数据冒险
  4. 避免控制冒险

面试相关

  1. 进程和线程的区别
  2. 解释孤儿进程,僵死进程,惊群效应
  3. 进程间通信方式
  4. malloc、realloc、calloc的区别
  5. malloc与free的实现原理
  6. 内存管理,mmu实现
  7. kill一个进程的过程是什么样子的
  8. Unicode与utf-8的区别
  9. 大端小端是什么?能否用c++代码简单测试下当前os是大端还是小端的?
  10. 如果内存只有1G,但是进程要占2G是否可以?
  11. 内存对齐:内存对齐了解吗?给一个结构体,包含char、int、指针,sizeof有多大,为什么?如果想紧凑对齐,应该怎么做?
  12. 内存分布
  13. char和int的强制转换
  14. C++,左值,右值
  15. float 和 double 是怎么存储的?结合大小端说一下
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值