20145218 《信息安全系统设计基础》期中总结

20145218 《信息安全系统设计基础》期中总结

教材学习内容总结

Linux基础

grep 命令

  • 管道是一种通信机制,通常用于进程间的通信(也可通过socket进行网络通信),它表现出来的形式就是将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin)。
  • grep命令的一般形式为: grep [命令选项]... 用于匹配的表达式 [文件]...
  • 使用grep命令查找宏 grep -nr /usr/include

cheat 命令

  • “作弊命令”,简单备忘单
  • 它提供显示Linux命令使用案例,包括该命令所有的选项和简短但尚可理解的功能。

查看用户

  • $ who am i

创建用户

  • 要创建用户需要 root 权限,要用到 sudo 这个命令。使用这个命令有两个大前提,一是要知道当前登录用户的密码,二是当前用户必须在 sudo 用户组。$ sudo adduser 用户名

用户组

  • 在 Linux 里面每个用户都有一个归属(用户组),用户组简单地理解就是一组用户的集合,它们共享一些资源和权限,同时拥有私有资源。
  • 查看用户属于的用户组:$ groups 用户名或 $ cat /etc/group | sort
  • 将其它用户加入 sudo 用户组:
$ su 当前用户   //获得root权限
$ groups 待添加用户
$ sudo usermod -G sudo 待添加用户        //将用户添加到sudo用户组
$ groups 待添加用户

删除用户

  • $ sudo deluser 待删除用户 --remove-home

目录结构及文件基本操作

FHS 标准

  • FHS(英文:Filesystem Hierarchy Standard 中文:文件系统层次结构标准),定义了系统中每个区域的用途、所需要的最小构成的文件和目录同时还给出了例外处理与矛盾处理。
  • FHS 定义了两层规范,第一层是, / 下面的各个目录应该要放什么文件数据,例如 /etc 应该要放置设置文件,/bin 与 /sbin 则应该要放置可执行文件等等。
  • 第二层则是针对 /usr 及 /var 这两个目录的子目录来定义。例如 /var/log 放置系统登录文件、/usr/share 放置共享数据等等。

Linux 文件的基本操作

新建

$ touch 文件名
$ mkdir 目录名
使用 -p 参数,同时创建父目录(如果不存在该父目录):
$ mkdir -p father/son/grandson

复制

  • 使用cp(copy)命令复制一个文件或目录到指定目录。$ cp 文件名 指定目录
  • 要成功复制目录需要加上-r或者-R参数,表示递归复制:$ cp -r father family

删除

  • 使用rm(remove files or directories)命令,删除一个文件或目录:$ rm test

移动文件与文件重命名

  • 使用mv(move or rename files)命令,移动文件(剪切)。$ mv 源目录文件 目的目录

重命名文件

  • $ mv 旧的文件名 新的文件名:

正则表达式

  • 正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。
  • 基本语法:
  • 一个正则表达式通常被称为一个模式(pattern),为用来描述或者匹配一系列符合某个句法规则的字符串。
  • 选择:|竖直分隔符表示选择,例如"boy|girl"可以匹配"boy"或者"girl"
  • 数量限定
  • +表示前面的字符必须出现至少一次(1次或多次)
  • ?表示前面的字符最多出现一次(0次或1次)
  • 代表前面的字符可以不出现,也可以出现一次或者多次(0次、或1次、或多次)
  • 范围和优先级:()圆括号可以用来定义模式字符串的范围和优先级,这可以简单的理解为是否将括号内的模式串作为一个整体。
  • 特殊字符
字符      描述
       将下一个字符标记为一个特殊字符、或一个原义字符。
^       匹配输入字符串的开始位置。
$       匹配输入字符串的结束位置。
{n}     n是一个非负整数。匹配确定的n次。
{n,}    n是一个非负整数。至少匹配n次。
{n,m}   m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。
*       匹配前面的子表达式零次或多次。
+       匹配前面的子表达式一次或多次。
?       匹配前面的子表达式零次或一次。

[^xyz]  排除型(negate)字符集合。匹配未列出的任意字符。
[a-z]   字符范围。匹配指定范围内的任意字符。
[^a-z]  排除型的字符范围。匹配任何不在指定范围内的任意字符。

Linux环境下编程基础

Vim编辑器

vim常用基本模式介绍

  • 普通模式(Normal mode)
  • 在普通模式中,用的编辑器命令,比如移动光标,删除文本等等。这也是Vim启动后的默认模式。这正好和许多新用户期待的操作方式相反(大多数编辑器默认模式为插入模式)。
  • 在普通模式中,有很多方法可以进入插入模式。比较普通的方式是按a(append/追加)键或者i(insert/插入)键。
  • 插入模式(Insert mode)
  • 在这个模式中,大多数按键都会向文本缓冲中插入文本。大多数新用户希望文本编辑器编辑过程中一直保持这个模式。
  • 在插入模式中,可以按ESC键回到普通模式。
  • 命令行模式(Command line mode)
  • 在命令行模式中可以输入会被解释成并执行的文本。例如执行命令(:键),搜索(/和?键)或者过滤命令(!键)。在命令执行之后,Vim返回到命令行模式之前的模式,通常是普通模式。

游标移动

按键      说明
h       左
l       右(小写L)
j       下
k       上
w       移动到下一个单词
b       移动到上一个单词

插入模式

  • 在普通模式下使用下面的键将进入插入模式,并可以从相应的位置开始输入
命令      说明
i       在当前光标处进行编辑
I       在行首插入
A       在行末插入
a       在光标后插入编辑
o       在当前行后插入一个新行
O       在当前行前插入一个新行
cw      替换从光标所在位置后到一个单词结尾的字符

保存文档

  • 从普通模式输入:进入命令行模式,输入w回车,保存文档。输入:w 文件名可以将文档另存为其他文件名或存到其它路径下

退出vim

  • 从普通模式输入:进入命令行模式,输入wq回车,保存并退出编辑
命令              说明
:q!             强制退出,不保存
:q              退出
:wq!            强制保存并退出
:w <文件路径>   另存为
:saveas 文件路径    另存为
:x              保存并退出
:wq             保存并退出
  • 普通模式下输入Shift+zz即可保存退出vim

删除文本

  • 进入普通模式,使用下列命令可以进行文本快速删除
命令      说明
x       删除游标所在的字符
X       删除游标所在前一个字符
Delete  同x
dd      删除整行
dw      删除一个单词(不适用中文)
d$或D    删除至行尾
d^      删除至行首
dG      删除到文档结尾处
d1G     删至文档首部
  • 在命令之前加上数字,表示一次删除多行,如:2dd表示一次删除2行

gcc编译器

常用选项

-c      只编译不链接,生成目标文件.o
-S      只编译不汇编,生成汇编代码
-E      只进行预编译,不做其他处理
-g      在可执行程序中包含标准调试信息
-o file 将file文件指定为输出文件
-v      打印出编译器内部编译各过程的命令行信息和编译器的版本
-I dir  在头文件的搜索路径列表中添加dir目录

库选项

  • -static 进行静态编译,即链接静态库,禁止使用动态库
  • -shared 1.可以生成动态库文件
    2.进行动态编译,尽可能的链接动态库,没有动态库时才会链接同名静态库
  • -L dir 在库文件的搜索路径列表中添加dir目录
  • -lname 链接称为libname.a或者libname.so的库文件。
  • 如果两个库文件都存在,根据编译方式是static还是shared进行链接
  • -fPIC 生成使用相对地址的位置无关的目标代码,
  • (-fpic) 然后通常使用gcc的-static选项从该pic目标文件生成动态库文件。

编译过程

  • 预处理: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** 创建可执行目标文件

gdb调试

  • 使用GCC编译时要加“-g”参数
  • 基本命令
  • gdb programm(启动GDB)
  • b 设断点(4种断点:行断点、函数断点、条件断点、临时断点)
  • run 开始运行程序
  • bt 打印函数调用堆栈
  • p 查看变量值
  • c 从当前断点继续运行到下一个断点
  • n 单步运行
  • s 单步运行
  • quit 退出GDB

四种断点:

  • 行断点:b [行数或函数名] <条件表达式>
  • 函数断点:b [函数名] <条件表达式>
  • 条件断点:b [行数或函数名] <if表达式>
  • 临时断点:tbreak [行数或函数名] <条件表达式>

Makefile

Makefile定义

  • makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至进行更复杂的功能操作。
  • makefile带来的好处就是——“自动化编译”,makefile文件需要按某种语法进行编写,文件中需要说明如何编译各个源文件并链接生成可执行文件,要求定义源文件之间的依赖关系。

Makefile 基本规则

  • Makefile的一般写法:
test(目标文件): prog.o code.o(依赖文件列表)
tab(至少一个tab的位置) gcc prog.o code.o -o test(命令)   
.......

使用带宏的 Makefile

  • Makefile还可以定义和使用宏(也称做变量),从而使其更加自动化,更加灵活,在Makefile中定义宏的格式为:macroname = macrotext
  • 使用宏的格式为:$(macroname)

信息的表示和处理

  • 缓冲区溢出漏洞:计算机的表示法是用有限数量的位来对应一个数字编码,当结果太大不能表示时就会发生溢出。人为的溢出是有一定企图的,攻击者写一个超过缓冲区长度的字符串,植入到缓冲区这时可能会出现两种结果:一是过长的字符串覆盖了相邻的存储单元,引起程序运行失败,严重的可导致系统崩溃;另一个结果就是利用这种漏洞可以执行任意指令,甚至可以取得系统root特级权限。

信息存储

大端法&小端法

  • 最低有效字节在最前面的方式称为小端法,最高有效字节在最前面的方式称为大端法。字节顺序是网络编程的基础,小端是“高对高、低对低”,大端与之相反。

布尔代数

  • 布尔代数起源于数学领域,是一个用于集合运算和逻辑运算的公式:〈B,∨,∧,¬ 〉。其中B为一个非空集合,∨,∧为定义在B上的两个二元运算,¬为定义在B上的一个一元运算。
  • 通过布尔代数进行集合运算可以获取到不同集合之间的交集、并集或补集,进行逻辑运算可以对不同集合进行与、或、非。
  • 所有逻辑运算都可以用与、或、非表达(最大式、最小式),而与或非可以用“与非”或“或非”表达,所以,只要一个与非门,就可以完成所有的逻辑运算。

C语言中的逻辑运算

  • || OR
  • && AND
  • ! NOT
  • 逻辑运算认为,所有非零参数都为TRUE,参数0为FALSE,返回值分别为1和0。
  • 逻辑运算符和对应的位运算之间的重要区别是,如果对第一个参数求值就能确定表达式的结果,那么就不会对第二个参数求值,避免运算结果的错误。

C语言中的移位运算

  • 对于一个位表示为[xn-1,xn-2,…,x0]的操作数x,C表达式x<>k,但是它的行为有点微妙。一般而言,机器支持两种形式的右移:逻辑右移和算术右移。逻辑右移在左端补k个0,得到的结果是[0,…,0,xn-1,xn-2,…,xk]。算术右移是在左端补k个最高有效位的值,得到的结果是[xn-1,…,xn-1,xn-1,xn-2,…,xk]。

整数表示

无符号数的编码

  • 假设一个整数数据类型有w位。我们可以将位向量写成x→,表示整个向量,或者写成[xw-1 ,xw-2,…,x0],表示向量中的每一位。把x→看做一个二进制表示的数,就获得了x→的无符号表示。
  • 无符号二进制有一个很重要的属性,就是每个介于0~2^w-1之间的整数都有唯一一个w为的值编码,函数为一个双射。

补码编码

  • 最常见的有符号数的计算机表示方式就是补码形式。在这个定义中,将字的最高有效位解释为负权。
  • 所能表示的数值范围[-2(w-1)~2(w-1)-1],在可表示的范围内每个数字 都有一个唯一的w位的补码编码,函数为一个双射。

符号数的其他表示方法

  • 反码:除了最高有效位的权是-(2w-1-1)而不是-2w-1,它和补码是一样的
  • 原码:最高有效位是符号位用来确定剩下的位应该取负权还是正权。

截断数字

  • 将一个w位的数假设我们不用额外的位来扩展一个数值,而是减少表示一个数字的位数。x=[xw-1 ,xw-2,…,x0]截断为一个k位的数字时,会丢弃高w-k位,得到一个位向量[xk-1 ,xk-2,…,x0],截断一个数字可能会改变他的值——溢出的一种形式。

整数运算

无符号加法

  • 考虑两个非负整数x和y,满足0≤x, y≤2w-1。每个数都能表示为w位无符号数字。然而,如果计算它们的和,我们就有一个可能的范围0≤x + y≤2w+1-2。表示这个和可能需要w + 1位。这种持续的“字长膨胀”意味着,要想完整地表示算术运算的结果,要对字长做限制。
  • 无符号运算可以被视为一种模运算形式。无符号加法等价于计算和模上2w。可以通过简单的丢弃x + y的w + 1位表示的最高位,来计算这个数值。

无符号乘法

  • 范围在0≤x, y≤ 2w-1内的整数x和y可以表示为w位的无符号数,但是它们的乘积x · y的取值范围为0到(2w-1)2 = 22w-2w+1+1之间。这可能需要2w位来表示。不过,C语言中的无符号乘法被定义为产生w位的值,就是2w位的整数乘积的低w位表示的值。可以看作等价于计算乘积模2w。

浮点数

  • 浮点表示对形如V = x×2y的有理数进行编码。它对执行涉及非常大的数字(|V |>>0)、非常接近于0(|V |<<1)的数字,以及更普遍地作为实数运算的近似值的计算,是很有用的。

IEEE浮点表示

  • IEEE浮点标准用V = (-1)^s × M × 2^E的形式来表示一个数:
  • 符号:s决定这个数是负数(s=1)还是正数(s=0),而对于数值0的符号位解释作为特殊情况处理。
  • 尾数:M是一个二进制小数,它的范围是1~2-ε,或者是0~1-ε。
  • 阶码:E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)。
  • 将浮点数的位表示划分为三个字段,分别对这些值进行编码:
  • 一个单独的符号位s直接编码符号s。
  • k位的阶码字段exp = ek-1…e1e0编码阶码E。
  • n位小数字段frac = fn-1…f1 f0编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0。

程序的机器级表示

  • X86 寻址方式经历三代:
  1. DOS时代的平坦模式,不区分用户空间和内核空间,很不安全
  2. 8086的分段模式
  3. IA32的带保护模式的平坦模式
  • Linux使用平坦寻址方式,使程序员将整个存储空间看做一个大的字节数组。
  • ISA:指令集体系结构,机器级程序的格式和行为,它定义了处理器状态、指令的格式以及每条指令对状态的影响。
  • 程序计数器(通常称为PC,用%eip表示),指示将要执行的下一条指令在存储器中的地址。
  • 整数寄存器文件:存储地(对应于C语言的指针)或整数数据。
  • 条件码寄存器:保存着最近执行的算数或逻辑指令的状态信息,用来实现控制或者数据流中的条件变化。
  • 浮点寄存器:用来存放浮点数据。
  • 编译过程:
  • C预处理器插入宏和头文件:gcc -E xxx.c -o xxx.i
  • 编译器产生源代码的汇编代码:gcc -S xxx.i -o xxx.s
  • 汇编器化成二进制目标代码:gcc -c xxx.s -o xxx.o
  • 链接器生成最终可执行文件:gcc xxx. -o xxx
  • 用objdump -d xxx.o -o xxx.s 反汇编

访问信息

寄存器

  • 一个IA32中央处理单元(CPU)包含一组8个存储32位值的寄存器。用来存储整数数据和指针。
%eax    %ax (%ah %al)  通用寄存器
%ecx    %cx (%ch %cl)  通用寄存器
%edx    %dx  (%dh %dl)   通用寄存器
%ebx    %bx  (%bh %bl)   通用寄存器
%esi    %si             用来操纵数组
%edi    %di             用来操纵数组
%esp    %sp             操纵栈帧
%ebp    %bp             操纵栈帧

寻址方式

  • 根据操作数的不同类型,寻址方式可分为以下三种:
  • 立即数寻址方式:操作数为常数值,写作$后加一个整数。
  • 寄存器寻址方式:操作数为某个寄存器中的内容
  • 存储器寻址方式:根据计算出来的地址访问某个存储器的位置
  • 寻址模式:一个立即数偏移Imm,一个基址寄存器Eb,一个变址寄存器Ei,一个比例因子s(必须为1,2,4,8)有效地址计算为:Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s

数据传送指令

  • MOV相当于C语言的赋值'=':mov S,D S中的字节传送到D中
  • push和pop:

    pushl   S       R[%esp] ← R[%esp]-4
                    M[R[%esp]] ← S

    popl    D       D ← M[R[%esp]]
                    R[%esp] ← R[%esp]+4
  • 栈顶元素的地址是所有栈中元素地址中最低的,后进先出;
  • 指针就是地址;局部变量保存在寄存器中。

算术和逻辑操作

加载有效地址

  • leal,从存储器读数据到寄存器,而从存储器引用的过程实际上是将有效地址写入到目的操作数。目的操作数必须是一个寄存器。

一元操作和二元操作

  • 一元操作:只有一个操作数,既是源又是目的,可以是一个寄存器或者存储器。
  • 二元操作:第二个操作数既是源又是目的,两个操作数不能同时是存储器。

移位

  • 先给出位移量,然后是位移的数值,可进行算数和逻辑右移。移位操作移位量可以是立即数或%cl中的数。

条件码

  • 描述最近的算数或者逻辑操作的属性,可以检测这些寄存器来执行条件分支指令。
    CF:进位标志,最近操作使高位产生进位,用来检测无符号操作数的溢出
    ZF:零标志,最近操作得出的结果为0
    SF:符号标志,最近操作得到的结果为负数
    OF:溢出标志,最近操作导致一个补码溢出-正溢出或负溢出。
  • leal不改变条件码寄存器
  • CMP与SUB的区别:CMP也是根据两个操作数之差设置条件码,但只设置条件码而不更新目标寄存器
  • 有条件跳转的条件看状态寄存器(教材上叫条件码寄存器)

v过程

  • 数据传递、局部变量的分配和释放通过操纵程序栈来实现。

栈帧结构

为单个过程- 分配的栈叫做栈帧,寄存器%ebp为帧指针,而寄存器指针%esp为栈指针,程序执行时栈指针移动,大多数信息的访问都是相对于帧指针。

  • 栈向低地址方向增长,而栈指针%esp指向栈顶元素。

转移控制

  • call:目标是指明被调用过程起始的指令地址,效果是将返回地址入栈,并跳转到被调用过程的起始处。
  • ret:从栈中弹出地址,并跳转到这个位置。
  • 函数返回值存在%eax中

处理器体系结构

Y86指令集体系结构

程序员可见的状态

  • Y86程序中的每条指令都会读取或修改处理器状态的某些部分,称为程序员可见状态。其中包括:
  • 8个程序寄存器:%eax,%ecx,%edx,%ebx,%esi,%edi,%esp和%ebp。
  • 条件码:ZF(零)、SF(符号)、OF(有符号溢出)
  • 程序计数器(PC):存放当前正在执行的指令的地址
  • 存储器:很大的字节数组,保存着程序和数据。Y86系统用虚拟地址来引用存储器的位置,硬件和操作系统软件联合起来将虚拟地址翻译成实际或者物理地址。
  • 状态码(stat):表明程序执行的总体状态。(异常处理)

Y86指令

  • movl:irmovl、rrmovl、mrmovl、rmmovl,分别 显式地指明源和目的地的格式。第一个字母表明源的类型,i(立即数)、r(寄存器)或m(存储器 ),第二个字母代表目的,可以是r或者m。
  • OPl(整数操作指令):addl、subl、andl和xorl。只对寄存器数据进行操作,同时还设置条件码。
  • jXX(跳转指令):jmp、jle、jl、je、jne、jge、jg,根据分支指令的类型和条件码的设置来选择分支。
  • cmovXX(条件传送指令):cmovle、cmovl、cmove、cmovne、cmovge和comvg,与寄存器-寄存器传送指令rrmovl一样,但只有当条件码满足所需要的约束时才会更新目的寄存器的值。
  • call和ret:call指令将返回地址入栈,然后跳转到目的地址。ret指令从这样的过程调用中返回。
  • pushl和popl:入栈和出栈。
  • halt:停止指令的执行。

指令编码

  • 指令的字节级编码规则:高4位为代码部分,低四位为功能部分,功能值只有在一组相关指令共用一个代码时才有用。
  • 8个程序寄存器当中,每个都有相应的0~7的寄存器标识符。程序寄存器存在CPU中的一个寄存器文件中,这个文件就是一个小的、以寄存器ID作为地址的随机访问存储器。
  • 附加寄存器指示符字节:指定一个或者两个寄存器。
  • 附加4字节的常数字:作为irmovl的立即数数据,rmmovl和mrmovl的地址指示符的偏移量,以及分支指令和调用指令的目的地址。
  • 注意:分支指令和调用指令的目的地址是一个绝对地址。所有整数采用小端法编码。

逻辑设计和硬件控制语言HCL

  • 实现一个数字系统需要的三个组成部分:计算对位进行操作的函数的组合逻辑、存储位的存储器元素,以及控制存储器元素更新的时钟信号。

计算对位进行操作的函数的组合逻辑

  • 多路复用器:(s&&a)||(!s&&b)(控制信号为输入位S,S为1时输出a,s为0时输出b)
  • 字级的组合电路:HCL中,所有字级的信号都声明为int,不指定字的大小。
  • 多路复用函数是用情况表达式来描述的。

    [
    select1 :   expr_1
    select2 :   expr_2
        ......
    selectk :   expr_k
    ]
  • 算数/逻辑单元(ALU):三个输入分别为标号为A和B的两个数据输入,和一个控制输入。(注意减法的操作数顺序)
  • 集合关系:判断集合关系的通用格式是iexpr in {iexpr1,iexpr2,....iexprk}(其中被测试和待匹配的值均为整数表达式。)

存储器和时钟

  • 为了产生时序电路,必须引入按位存储信息的设备,存储设备由同一个时钟信号控制。
  • 随机访问存储器:存储多个字,用地址来选择应该读写哪个字。
  • 时钟寄存器:存储单个位或字,时钟信号控制寄存器加载输入值。
  • 寄存器首先保持稳定(输出等于当前状态),时钟上升沿来到时,加载输入信号。Y86处理器用时钟寄存器保存程序计数器(PC),条件代码(CC)和程序状态(Stat)。
  • 寄存器文件有两个读端口(A和B),一个写端口(W),允许同时进行多端口读写操作。

Y86顺序实现

将处理组织成阶段

  • 取指:从存储器读取指令字节,地址为程序计数器(PC)的值。指令指示符字节两个四位部分,称为icode(指令代码)和ifun(指令功能)。vaIP(下一条指令的地址)=PC+已取出指令的长度。
  • 译码:从寄存器文件读入最多两个操作数,得到valA和/或valB。
  • 执行:算数逻辑单元(ALU)根据ifun的值执行指令指明的操作,计算存储器引用的有效地址,或者增加或减少栈指针。得到的值称为valE。也可根据条件码执行跳转。
  • 访存:将数据写入存储器,或者从存储器读出数据。读出的值为valM。
  • 写回:最多可以写两个结果到寄存器文件。
  • 更新PC:将PC设置成下一条指令的地址。

SEQ的时序

  • Y86指令集的计算原则:处理器从来不需要为了完成一条指令的执行而去读由该指令更新了的状态。

SEQ阶段的实现

  • 取指阶段:包括指令存储器硬件单元。
  • 译码和写回阶段:寄存器文件,支持同时进行两个读和两个写,每个端口有一个地址连接(寄存器ID)和一个数据连接(32根线路),既可以作为寄存器文件的输出字,又可以作为他的输入字。
  • 执行阶段:算数逻辑单元(ALU),输出为valE信号。
  • 访存阶段:读或者写程序数据,两个控制块产生存储器地址和存储器输入数据的值。另外两个块产生控制信号表明应该执行读还是写操作。当执行读操作时,数据存储器产生valM。
  • 更新PC阶段:产生程序计数器的新值,依据指令的类型和是否要选择分支,新的PC可能是valC、valM或者valP。

存储器层次结构

存储技术

随机访问存储器(RAM)

  • 静态RAM(SRAM):用来作为高速缓存存储器,每个位存储在一个双稳态的存储器单元里。双稳态:电路可以无限期的保持在两个不同的电压配置或者状态之一。只要供电,就会保持不变。
  • 动态RAM(DRAM):用来作为主存以及图形系统的帧缓冲区。将每个位存储为对一个电容的充电,当电容的电压被扰乱之后,他就永远都不会再恢复了。暴露在光线下会导致电容电压改变。
  • SRAM与DRAM的对比:
SRAM                    DRAM
不需要刷新               以纳秒为周期刷新
存取速度快               存取速度慢
对光电噪声不敏感          光电因素易导致电压改变
晶体管多密集度低          电容小,密集度高
功耗贵代价高             功耗低
  • 传统的DRAM:芯片中的单元(位)被分成了d个超单元,每个超单元都由w个DRAM单元组成, 一个d*w的DRAM共存储dw位信息。超单元被组织成一个r行c列的长方形阵列,rc=d。每个超单元的地址用(i,j)来表示(从零开始)。设计成二维矩阵是为了降低芯片上地址引脚的数量。
  • 访问主存
  • 总线:数据流通过称为总线的共享电子电路在处理器和DRAM之间传送。是一组并行的导线,能携带地址、数据和控制信号。
  • 总线事务:CPU和主存之间的数据传送的过程。读事务,从主存传数据到CPU;写事务,从CPU传数据到主存。
  • I/O桥:将系统总线的电子信号翻译成存储器总线的电子信号。系统总线连接CPU和I/O桥,控制总线连接I/O桥和主存。

非易失性存储器(ROM

  • 如果断电,DRAM和SRAM都会丢失信息,非易失性存储器——只读存储器:ROM。ROM是以他们能够被重编程的次数和对他们重编程的机制来区分的。
  • 可编程ROM(PROM):只能被编程一次。PROM每个存储单元有一种熔丝,只能用高电流熔断一次。
  • 可擦写可编程ROM(EPROM):紫外线光照射过窗口,EPROM就被清除为0,被擦除和重编程的次数为1000次。
  • 电子可擦除ROM(EEPROM):不需要一个物理上独立的编程设备,因此可以直接在印制电路卡上编程,能够编程的次数为10^5。
  • 闪存:基于EEPROM,为大量的电子设备提供快速而持久的非易失性存储。

磁盘存储

  • 磁盘构造:磁盘由盘片构成,表面覆盖着磁性记录材料,中央有一个可以旋转的主轴 ,旋转速率大约为5400-15000每分钟。磁盘的每个表面是一组称为磁道的同心圆组成,每个磁道被划分为一组扇区,扇区之间由一些间隙隔开,间隙存储用来标识扇区的格式化位。
  • 磁盘容量由以下技术因素决定:
  • 记录密度(位/英寸):磁道一英寸的段中可以放入的位数。
  • 磁道密度(道/英寸):从盘片中心出发半径上一英寸的段内可以有的磁道数
  • 面密度(位/平方英寸):记录密度与磁道密度的乘积。
  • 磁盘操作:磁盘用读/写头来读写存储在磁性表面的位,而读写头连接到一个传动臂 一端,通过移动转动臂将读写头定位在磁道上的机械运动称为寻道。磁盘以扇区大小的块来读写数据,对扇区的访问时间有三个主要的组成部分:
  • 寻道时间:转动臂将读/写头定位到包含目标扇区的磁道上所需时间。
  • 旋转时间:驱动器等待目标扇区的第一个位旋转到读/写头下的时间。最大为1/RPM,平均旋转时间是Tmax的一半。
  • 传送时间:读写并传送该扇区内容的时间。平均传送时间为:1/RPM*1/平均扇区数
  • 逻辑磁盘块:现代磁盘将盘面的构造视为一个B个扇区大小的逻辑块序列,磁盘控制器维护着逻辑块号和实际磁盘扇区之间的映射关系。逻辑块号可识别为一个盘面、磁道、扇区三元组,唯一的标识了相对应的物理扇区。内存可以看成字节数组、磁盘可以看成块数组。
  • 连接到I/O设备:所有的I/O设备都是通过I/O总线连接到CPU和主内存。有三种不同类型:
  • 通用串行总线:一个广泛的使用标准,用于连接各种外围I/O设备。
  • 图形卡(或适配器):包含硬件和软件逻辑,代表CPU在显示器上画像素。
  • 主机总线适配器: 将一个或者多个磁盘连接到I/O总线,使用一个特别的主机总线接口定义的通信协议。
  • 访问磁盘:CPU使用一种称为存储器映射I/O的技术向I/O设备发出命令,地址空间中为I/O设备通信保留的地址称为I/O端口。

固态硬盘

  • 固态硬盘是一种基于闪存的存储技术。一个硬盘包由一个或者多个闪存芯片和内存翻译层组成,闪存芯片替代旋转磁盘中的机械驱动器,而闪存翻译层将对逻辑块的请求翻译成对底层物理设备的访问

局部性

  • 局部性原理:一个编写良好的计算机程序倾向于引用邻近于其他最近引用过的数据项,或者最近引用过的数据项本身。有良好局部性的程序比局部性差的程序运行的更快,在硬件层引入高速缓存存储器就体现了局部性原理。
  • 对程序数据引用的局部性
  • 时间局部性(temporal locality):被引用过一次的存储器位置在未来会被多次引用(通常在循环中)。
  • 空间局部性(spatial locality):如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。
  • 一个连续向量中,每隔k个元素进行访问,被称为步长为k的引用模式,具有步长为1的引用模式称为顺序引用模式,随着步长增加空间局部性下降。
  • 双重嵌套循环按照行优先顺序读取数组元素。(因为C数组在存储器中是按照行顺序来存放的)

取指令的局部性

  • 程序指令是存放在存储器中的,CPU读取这些指令的过程中评价一个程序关于取指令的局部性。
  • 代码区别与程序数据的一个重要属性就是在运行时指令是不能被修改的。

局部性小结

  • 重复引用同一个变量的程序有良好的时间局部性
  • 对于具有步长为k的引用模式的程序,步长越小,空间局部性越好。
  • 对于取指令来说,循环具有良好的时间和空间局部性。循环体越小,迭代次数越多局部性越好。

存储器层次结构

存储器层次结构中的缓存

  • 高速缓存是一个小而快速的存储设备,作为存储在更大、更慢的设备中的数据对象的缓冲区域。每一层存储器被划分成连续的数据对象片,称为块,每个块都有唯一的对象和名字。数据总是以块大小为传送单元在第k层和第k+1层之间来回拷贝。
  • 缓存命中:当程序需要第k+1层的某个数据对象d时,首先在当前存储的第k层的一个块中查找d,如果d刚好在第k层中,则称为缓存命中。
  • 缓存不命中:如果k层中没有缓存数据d,则称为缓存不命中,此时要从k+1层取出包含d的块,可能会覆盖(替换/驱逐)现在的一个块(牺牲块)。决定该替换哪个快是缓存的替换策略来控制的。(例如,随机替换策略/LRU策略)
  • 缓存不命中的种类
  • 强制性不命中/冷不命中:第k层缓存是空的(冷缓存),只是短暂的状态,不会在反复访问存储器使得缓存暖身之后的稳定状态出现。
  • 冲突不命中:第k+1层的第i块,必须放置在第k层的块(i mod 4)中,这种限制性的放置策略引起冲突不命中。

存储器层次结构概念小结

  • 利用时间局部性: 一旦一个数据在第一次不命中时被拷贝到缓存中,我们就会期望后面对该目标有一系列的访问命中。
  • 利用空间局部性:块通常包含多个数据对象,我们通常期望后面对该块中其他对象的访问能够补偿不命中后拷贝该块的花费。

高速缓存存储器

通用的高速缓存存储器结构

  • 一个计算机系统每个存储地址有m位,形成M=2^m个不同的地址。
  • 高速缓存被组织成一个有S=2s个高速缓存组的数组,每个组包含E个高速缓存行,每个行是由一个B=2b字节的数据块、一位有效位以及t=m-(b+s)个标记位组成,唯一标识存储在这个高速缓存行中的块。
  • 高速缓存的结构用元组(S,E,B,m)来描述,高速缓存的大小C = S * E * B。
  • s个组索引位:一个无符号整数,说明字必须存储在哪个组中。
  • t个标记位:组中的哪一行包含这个字。
  • b个块偏移位:在B个字节的数据块中的字偏移。

直接映射的高速缓存

  • 每组只有一行(E=1)的高速缓存称为直接映射高速缓存。高速缓存确定一个请求是否命中,然后抽取出被请求字的过程分为三步:1)组选择,2)行匹配,3)字抽取。
  • 直接映射高速缓存中的组选择:高速缓存从要抽取的字的地址中抽取出S个组索引位,这些位被解释成一个对应于一个组号的无符号整数。
  • 直接映射高速缓存中的行匹配:当且仅当设置了有效位,而且高速缓存行标记与w的地址中的行标记相匹配时,这一行中包含w的一个拷贝。
  • 直接映射高速缓存中的字抽取:块偏移位提供了所需要的字的第一个字节的偏移。
  • 直接映射高速缓存中不命中时的行替换:需要从存储器层次结构中的下一层取出被请求的块,然后将新的块存储在组索引位指示的组中的一个高速缓存行中。

高速缓存参数的性能影响

  • 不命中率:不命中数量/引用数量
  • 命中率:1-不命中率
  • 命中时间:从高速缓存传送一个字到CPU所需的时间,包括组选择,行匹配,字抽取的时间。
  • 不命中处罚:由于不命中所需要的额外时间。

影响性能的因素

  • 高速缓存大小的影响:较大的高速缓存可能会提高命中率,但使大存储器运行的更快是更难一些的。
  • 块大小的影响:较大的块能利用程序中可能存在的空间局部性,帮助提高命中率;但块越大意味着高速缓存行较少,损害时间局部性。
  • 相联度的影响:相联度较大(E值较大)优点是降低了高速缓存由于冲突不命中出现抖动的可能性,但成本较高。
  • 写策略的影响:直写高速缓存易实现,而且能使用独立于高速缓存的写缓冲区,用来更新存储器,不命中开销小。写回高速缓存引起的传送比较少,允许更多的到存储器的宽带用于执行DMA的I/O设备。越下层越可能使用写回。

链接

与静态库链接

  • 静态库:将所有相关的目标模块打包成一个单独的文件,称为静态库。在unix系统中,静态库以一种称为存档的特殊文件格式存放在磁盘中。
  • 为了创建该库,使用AR工具:ar rcs libvector.a addvec.o multvec.o
  • 为了创建可执行文件,需要编译链接输入文件*.o 和libvector.a:
    gcc -O2 -c .c
    gcc -static -o p2 .o ./libvector.a
  • 加载可执行目标文件:./p

与动态库链接

  • 共享库:一个目标模块,在运行时可以加载到任意的存储器地址,并和一个在存储器中的程序链接起来。这个过程称为动态链接,是由一个叫做动态链接器的程序来执行的。
  • 创建动态库:gcc -shared -fPIC -o libvector.so addvec.c multvec.c
  • 将动态库链接到程序中:gcc -o p2 *.c ./libvector.so

易错习题总结

1.填空:Linux Bash中,把ls命令显示当前目录的结果存入ls.txt的命令输出重定向命令是(ls > ls.txt)
2.填空:Linux Bash中,zip命令使用(-e)参数可以创建加密压缩包。
3.man -k 填空: 数据结构中有二分查找算法,C标准库中有这个功能的函数,这个函数是(bsearch)
4.3.6 填空: 指令leal 8(%ecx,%eax,2), %edx 存储在%edx中的值为( 8+2x+y )注意哪个是x,哪个是y
5.CH04 填空:bool s= r1 || r2 || r=5 等价于(bool s = r in {1,2,5} )
6.6.8 填空:代码的步长为:( N*N )注意步长定义
7.CH06 判断:SRAM和DRAM掉电后均无法保存里面的内容。( ok )

其他(感悟、思考等,可选)

自己的收获:在这两个月的学习中,感觉每周的学习都很充实,都能真真切切学到东西,而每周的考试也起到了督促我们更加认真阅读教材,自己运行代码并理解的作用,在考试的过程中有些问题是自己在书本上看到可以答出来的,会很有成就感,而每次考试有不会的题目也是正常,可以让我们看到自己在有的方面确实是不足,那些相应的题目便提醒着我们在哪个方面应该补充。有些内容是通过看书一遍理解,就会解答习题的,还有一些内容是需要理解深化的,最重要的就是有些技能例如vi的使用,静态库动态库的制作,gcc,gdb,makefile的编写这些,是需要一遍一遍练习才能够掌握的,在实践中掌握的东西总是要比死记硬背记得更牢一些。总之很感谢老师的这种教学方法,做到了真正的学有所用。

自己的不足:每周的博客都是要边读书边写,否则读过去反倒是记不住博客里盖写什么,但是这样看书的效率也会相应降低,读书的注意力下降,想请教老师怎样高效专注的阅读。除了提高阅读能力以外就是提高动手能力,像vi、C语言程序编写都是上手很容易精通很难的硬功夫,还要经过practice、practice and practice。

建议:前几周每周的学习量比较大,而且需要练习的很多,比如各种Linux命令,一个周要记得太多了,也根本记不住,以后遇到的就翻翻以前的博客看看,遇不到的就算写在博客里也还是不知道怎么用,希望可以把每周的任务平均一点,又能练习又好掌握。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 200/200 2/2 20/20
第二周 300/500 3/4 18/38
第三周 200/1000 4/7 22/60
第四周 150/1300 4/9 30/90
第五周 200/500 5/4 18/38
第六周 120/1000 6/7 22/60
第七周 120/1300 7/9 30/90
第八周 50/1300 8/9 30/90

参考资料

原文地址:https://www.cnblogs.com/senlinmilelu/p/6035491.html