信息=位+上下文
计算机中的信息都是有二进制数字表达的,而因为这些二进制位所处的位置不同,是有符号数还是无符号数,是大端法还是小端法,由于具体的解释不同,造成的结果也不同。
之后的学习就是如何读写位,和上下文如何对应。
IEEE浮点标准——用V= (-1)^sM2^E来编码一个数。其中:
符号:s决定这个数是负数(s=1)还是正数(s=0),对于数值是0的符号位解释为特殊情况。
尾数:M是一个二进制小数。
阶码:E对浮点数加权,可以是负数。
寻址方式和几个操作,mov,push,pop,leal,还有跳转指令、控制转移指令等等。
这章里学习的是一个相对简单的处理器Y86,指令集比起IA32省略了很多。
Y86
一个简单的、可以称之为IA32指令集的子集的指令集;只包括四字节整数操作,寻址方式比较少。指令编码长度从1——6字节不等。指令集的一个重要性只就是字节编码必须具有唯一的解释。
关于指令结构,每条指令的第一个字节表明指令的类型;这个字节分为两个部分,每部分四位:高四位是代码部分(0——0xB),第四位是功能部分。这里补充一些缩写:立即数(i),寄存器(r)、存储器(m)。指令附加的寄存器指示符字节依次是数据源(如果是立即数,把这一位设置成0xf)、目的寄存器/基址寄存器。有些指令需要附加四字节的常数字,采用小端法(倒序)编码
局部性原理:
一个编写良好的计算机程序,常常倾向于引用临近于其他最近引用过的数据项的数据项,或者最近引用过的数据项本身。
分类:
- 时间局部性
- 空间局部性
量化评价一个程序中局部性的简单原则:
- 重复引用同一个变量的程序有良好的时间局部性
- 对于具有步长为k的引用模式的程序,步长越小,空间局部性越好
- 对于取指令来说,循环有好的时间和空间局部性。循环体越小,循环迭代次数越多,局部性越好。
存储器层次结构中心思想:每层存储设备都是下一层的“缓存”。
1.缓存命中
当程序需要第k+1层的某个数据对象d时,首先在当前存储在第k层的一个块中查找d,如果d刚好缓存在第k层中,就称为缓存命中。
该程序直接从第k层读取d,比从第k+1层中读取d更快。
2.缓存不命中
即第k层中没有缓存数据对象d。
这时第k层缓存会从第k+1层缓存中取出包含d的那个块。如果第k层缓存已满,就可能会覆盖现存的一个块
覆盖——替换/驱逐
替换策略:
- 随机替换策略-随机牺牲一个块
- 最近最少被使用替换策略LRU-牺牲最后被访问的时间距离现在最远的块。
3.缓存不命中的种类
(1)强制性不命中/冷不命中
即第k层的缓存是空的(称为冷缓存),对任何数据对象的访问都不会命中。
(2)冲突不命中
由于一个放置策略:将第k+1层的某个块限制放置在第k层块的一个小的子集中,这就会导致缓存没有满,但是那个对应的块满了,就会不命中。
(3)容量不命中
当工作集的大小超过缓存的大小时,缓存会经历容量不命中,就是说缓存太小了,不能处理这个工作集。
高速缓存存储器。
高速缓存是一个高速缓存组的数组,它的结构可以用元组(S,E,B,m)来描述:
S:这个数组中有S=2^s个高速缓存组 E:每个组包含E个高速缓存行 B:每个行是由一个B=2^b字节的数据块组成的 m:每个存储器地址有m位,形成M=2^m个不同的地址
除此之外还有标记位和有效位:
有效位:每个行有一个有效位,指明这个行是否包含有意义的信息 标记位:t=m-(b+s)个,唯一的标识存储在这个高速缓存行中的块 组索引位:s 块偏移位:b
高速缓存的结构将m个地址划分成了t个标记位,s个组索引位和b个块偏移位。
常用来搜索,结合管道使用。例句如下:
man -k k1 | grep k2 | grep 2
搜索同时含有k1和k2,且属于系统调用。
最后的数字意味着帮助手册中的区段,man手册共有8个区段,最常用的是123,含义如下:
1.Linux 2.系统调用 3.c语言
但是当单独用man语句的时候,想查看其中的单独某个区段内的解释时,用法是这样的:
man 3 printf
即查找c语言中printf的用法。
这条语句可以用来查找关键字,全文搜索,并且可以直接查找文件内的内容。其中:
vim是一种非常好用的编辑器,总共有六种基本模式,最常用的是普通模式、插入模式和命令行模式。需要熟悉这三种模式之间的切换方式:
普通→插入: i 或 a 插入→普通: Esc 或 Ctrl + [ 普通→命令行: : 命令行→普通:Esc 或 Ctrl + [
常用的进入、保存和退出指令:
进入:vim 文件名 保存:命令行模式 :w 退出:命令行模式 :q
常用动作:
删除:dd删除整行 复制:yy复制整行 粘贴:p
※实用功能:交换上下行——ddp,快速交换光标所在行与它下面的行。
常用选项
-c 只编译不链接,生成目标文件.o -S 只编译不汇编,生成汇编代码 -E 只进行预编译,不做其他处理 -g 在可执行程序中包含标准调试信息 -o file 将file文件指定为输出文件 -v 打印出编译器内部编译各过程的命令行信息和编译器的版本 -I dir 在头文件的搜索路径列表中添加dir目录
gdb:
编译过程
预处理:gcc –E hello.c –o hello.i; gcc –E调用cpp 生成中间文件 编 译:gcc –S hello.i –o hello.s; gcc –S调用ccl 翻译成汇编文件 汇 编:gcc –c hello.s –o hello.o; gcc -c 调用as 翻译成可重定位目标文件 链 接:gcc hello.o –o hello ; gcc -o 调用ld** 创建可执行目标文件
注意:使用GCC编译时要加“-g”参数,然后才能够用gdb调试
GDB最基本的命令有:
gdb programm(启动GDB) l 查看所载入的文件 b 设断点 info b 查看断点情况 run 开始运行程序 bt 打印函数调用堆栈 p 查看变量值 c 从当前断点继续运行到下一个断点 n 单步运行(不进入) s 单步运行(进入) quit 退出GDB
四种断点:
1.行断点 b [行数或函数名] <条件表达式> 2.函数断点 b [函数名] <条件表达式> 3.条件断点 b [行数或函数名] <if表达式> 4.临时断点 tbreak [行数或函数名] <条件表达式>
另外的调试工具:
cgdb,有单独的debug窗口,更方便查看
ddd,图形页面
Makefile的一般写法:
一个Makefile文件主要含有一系列的规则,每条规则包含以下内容:
需要由make工具创建的目标体,通常是可执行文件和目标文件,也可以是要执行的动作,如‘clean’;
要创建的目标体所依赖的文件,通常是编译目标文件所需要的其他文件。
创建每个目标体时需要运行的命令,这一行必须以制表符TAB开头
格式为:
test(目标文件): prog.o code.o(依赖文件列表) tab(至少一个tab的位置) gcc prog.o code.o -o test(命令) ....... 即: target: dependency_files command
定义变量的两种方式:
(1)递归展开方式 VAR=var (2)简单方式 VAR:=var
使用变量的格式为:
$(VAR)
正则表达式:
{n} 次数修饰,重复n次,具体如下:
?= {0,1}
+= {1, }
*= {0, } {m,n}:至少为m,至多为n
【参考资料:课本,之前博客,被老师介绍过的同学们的优秀笔记】
新的学习方法比之前应付老师应付自己的学习确有很大不同,感觉学到的知识比之前更多也更易使用。增强了课业内容的实用性,提高了实践能力;
本人感觉自己难以持之以恒,有时看书写博都很仓促,导致质量差强人意;也有很多知识点并未吃透,长时间放置一旁不予过问终会成一大问题,日后的学习里应该时刻警醒自己,许多细小的知识点不能马虎放过。
另外,平时看书时间较少,浪费时间较多;以后应该合理安排学习时间,保证效率。