20145207 《信息安全系统设计基础》期中总结
重点要说在前面:我是真的觉得这篇博客(http://www.cnblogs.com/20145207lza/p/6028924.html#3549061)写的很全了,在考虑一周两篇博客时,真的不知道写些什么!醉了。我有考虑写家庭作业,仔细看了书上的习题之后发现真的好多不会的,会的个别的简单题也没啥好发的,最后不会的题目参考了上一届学长学姐的博客才搞明白(http://www.cnblogs.com/20145238jym/p/5978377.html)真不是我说,这也没剩几道题留给我们了呀。唉!想优秀博客好难呀。真真的不想在带及格里面呆着了。。这时又想写个实验报告,然而还是分开的。我甚至都想些读书笔记了。最后敲了这个按章节的总结。老实讲,总结的博客千篇一律,没啥意思。感觉周四写的操作指令汇总挺好的(自恋)晚一点把实验报告写了,之后在研究研究书上的题吧。唉,我实践性太差。(最后说下,周四的http://www.cnblogs.com/20145207lza/p/6028924.html#3549061这个也一起算为这周总结,争取优秀,哈哈)
一、计算机系统概述
1.系统的硬件组成
-
总线——贯穿整个系统的一组电子管道,它携带信息字节并负责在各个部件间传递。通常总线被设计成传送定长的字节块,也就是字(word)。各个系统中字长不尽相同。
-
I/O设备
I/O设备是系统与外界的联系通道。
控制器是I/O设备本身中或是系统的主印刷电路板(主板)上的芯片组,而适配器则是一块插在主板插槽上的卡。
-
主存——临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据。物理上来说,主存是由一组DRAM芯片组成的;逻辑上来说,存储器由一个线性的字节数据组成,每个字节都有自己惟一的地址(数组索引),这些地址以0开始的。一般来说,组成程序的每条机器指令都由不定量的字节构成。
-
处理器——CPU,是解释(或执行)存储在主存中指令的引擎。处理器的核心是一个被称为程序计数器(PC)的字长大小的存储设备(或寄存器)。在任何一个时间点上,PC都指向主存中的某条机器语言指令。
CPU在指令要求下可能会执行这些操作:
加载:从主存拷贝一个字节或者一个字到寄存器,覆盖寄存器原来的内容
存储:从寄存器拷贝一个字节或者一个字到主存的某个位置,覆盖这个位置上原来的内容
更新:拷贝两个寄存器的内容到ALU,ALU将两个字相加,并将结果存放到一个寄存器中,覆盖该寄存器中原来的内容
I/O读:从一个I/O设备中拷贝一个字节或者一个字到一个寄存器
I/O写:从一个寄存器中拷贝一个字节或者一个字到一个I/O设备 转移:从指令本身中抽取一个字,并将这个字拷贝到程序计数器中,覆盖PC中原来的值
2.层次结构的存储设备
- 存储器分层结构的主要思想是:一个层次上的存储器作为下一层次上的存储器的高速缓冲。
3.操作系统提供的抽象表示
操作系统内核是应用程序和硬件之间的媒介。它提供三个基本的抽象概念:文件是对I/O设备的抽象概念;虚拟存储器是对主存和磁盘的抽象概念;进程是处理器、主存和I/O设备的抽象概念。
-
进程是操作系统对运行程序的一种抽象。并发运行实际上是一个进程的指令和另一个进程的指令交错执行的,操作系统实现这种交错执行的机制称为上下文切换。操作系统保存进程运行所需的所有状态信息,这些状态称为上下文。
-
一个进程可由多个线程组成。每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。
-
虚拟存储器 虚拟存储器是抽象概念,它为每个进程提供一个假象,好像每个进程都在独占地使用主存。每个进程看到的存储器都是一致的,称之为虚拟地址空间。
其基本思想是把一个进程虚拟存储器的内容存储在磁盘上,然后用主存作为磁盘的高速缓冲。
-
文件——是字节序列。每个I/O设备,包括磁盘,键盘,显示器,网络,等可以看成是文件。
- 信息,就是位加上下文。
4.系统,是相互交织的硬件和系统软件的集合体。
二、信息的表示与处理
我们重点研究三种数字:无符号数、补码和浮点数。
1.信息的存储
-
机器级程序将存储器视为一个非常大的字节数组,称为虚拟存储器(virtual memory)。存储器的每个字节都由一个唯一的数字来标识,称为地址(address),所有可能地址的集合就称为虚拟地址空间(virtual address space)。虚拟地址空间是一个展现给机器级程序的概念性映像(image),具体的实现使用的是随机访问存储器RAM,磁盘存储,特殊硬件和OS软件的结合,来为程序提供一个看上去统一的字节数组。
-
指值有两个方面:它的值和它的类型。它的值表示的是某个对象的位置,而它的类型表示那个位置上所存储对象的类型(如int,float)。
-
十六进制表示法 以0x或0X开头表示,字符A-F可大写可小写。
!掌握二进制和十六进制之间的转换,二进制四位与十六进制一位相对应。
-
每台计算机都有一个字长,指明整数和指针数据的标称大小。虚拟地址是以字来编码的。对于一个字长为n位的机器来说,虚拟地址的范围为0~2n-1,程序最多访问2n个字节。 如32位字长的计算机上,虚拟地址空间为4GB。
-
寻址和字节顺序
计算机和编译器使用不同的方式来编码数字。 (1)小端:低地址存放低字节。(2)大端:高地址存放低字节。
-
表示字符串、代码 a-z的ASC||码为0x61-0x7A。
-
布尔代数 用0、1表示真假,以研究逻辑推理的基本原则。 有:~=NOT;&=AND;|=OR;^=异或。
-
C语言中的位级运算
!!确定一个位级表达式的结果最好的方法,就是将十六进制的参数扩展成二进制标识并执行二进制运算,然后再转换回十六进制。
-
C语言中的逻辑运算
有:||、&&、!,分别对应于OR、AND和NOT运算。
注意:逻辑运算认为所有非零的参数都表示TRUE,而参数0表示FALSE。
-
C中的移位(移位运算从左至右结合。)
1)机器支持两种形式的右移:逻辑的算术的。逻辑右移k位在左端补k个0,[0,...0,xn-1,xn-2,...]。算术右移是在左端补k个最高有效位的拷贝,[xn-1,...xn-1,xn-2,...] 对于无符号数据,右移必须是逻辑的,有符号数则两者都可以,一般机器实现中实现为算术右移。
2)左移,就是移位后,在右边补0。
2.整数表示
-
要将一个无符号数转换为一个更大的数据类型,只要简单的在开头添加0;这种运算称为0扩展(zero extension)。要将一个二进制补码数字转换为一个更大的数据类型,执行符号扩展(sign extension),在表示中添加最高有效位的值。
-
在数据转换中,由大数据转换成小数据可能发生截断。弃高k位。对于一个无符号数x,截断它到k位的结果就相当于计算x mod 2k。
-
范围0,≤x,y≤2w-1内的整数x,y,可以被表示成w位的无符号数字,它们的乘积x.y的取值范围为0~(2w-1)2,这可能需要2w位来表示。
-
在大多数机器上,整数乘法指令相当慢,需要12或更多时钟周期,其他整数运算,如加法,减法,移位和位级运算,只需要1个时钟周期。因此编译器使用的一项重要的优化就是试着用移位和加法运算的组合来代替乘以常数因子的乘法。 整数除法比乘法更慢,需要30或者更多的时钟周期,除以2的幂也可以用移位运算来实现。我们可右移。对于无符号数和二进制补码数,分别使用逻辑移位和算术移位来达到目的。
-
在当前的计算机中,整数是以补码表示的。
3.浮点数
-
二进制小数 小数的二进制表示,二进制点左边的权形如2^i,而右边的数字的权形如1/2^i。
-
IEEE浮点表示
-
IA32处理器有特别的存储器元素,称为寄存器,当计算或使用浮点数时,用来保存浮点值。浮点数的取反就是对符号位取反。
IEEE标准定义了一些合理规则。定义1/-0将产生-∞,而定义1/+0会产生+∞。
IEEE标准中指定浮点运算行为方法的一个优势在于,它可以独立于任何具体的硬件或者软件实现。
浮点加法不具有结合性,这是缺少的最重要的群属性;浮点加法满足了单调性属性——如果a>=b,那么对于任何a\b以及x的值,除了NaN,都有x+a>=x+b。无符号或补码加法不具有这个实数(和整数)加法的属性。
三、程序的机器级表示
-
Intel现在称其指令集为IA32,即Intel 32位体系结构(Intel Architecture 32-bit),这个处理器也俗称为“x86”。
-
Linux使用了平面寻址方式(flat addressing),在这种寻址方式中,程序员将整个存储空间看做一个大的字节数组。在平面寻址中,对特殊寄存器的需求已经大为降低了。在大多数情况下,前六个寄存器都可以看作是通用寄存器,对它们使用的没有限制。
-
机器级代码
两种抽象:
指令集体系结构ISA——机器级程序的格式和行为,定义了处理器状态、指令的格式以及每条指令对状态的影响。
机器级结构使用的存储地址虚拟地址,存储器系统的实际实现是将多个硬件存储器和操作系统软件组合起来。
-
一个过程调用包括将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分。数据传递、局部变量的分配和释放是通过操纵程序栈来实现的。
-
反汇编器——查看目标代码文件的内容 带-d命令行标志的程序objdump可以实现:objdump –d code.o
-
过程
1)栈帧结构 2)转移控制
-
IA32程序用程序栈来支持过程调用。栈用来传递过程参数、存储返回信息、保存寄存器以供以后恢复之用,以及用于本地存储。为单个过程分配的那部分栈称为栈帧。栈帧的最顶端是以两个指针定界的,寄存器%ebp作为帧指针,而寄存器%esp作为栈指针。
-
esi、edi可以用来操纵数组,esp ebp用来操纵栈帧。 对于寄存器,特别是通用寄存器中的eax,ebx,ecx,edx,大家要理解32位的eax,16位的ax,8位的ah,al都是独立的,我们通过下面例子说明:
假定当前是32位x86机器,eax寄存器的值为0x8226,执行完addw $0x8266, %ax指令后eax的值是多少?
解析:0x8226+0x826=0x1044c, ax是16位寄存器,出现溢出,最高位的1会丢掉,剩下0x44c,不要以为eax是32位的不会发生溢出。
-
二进制文件可以用od命令查看,也可以用gdb的x命令查看。 有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看
od code.o | more
od code.o > code.txt
-
gcc -S 产生的汇编中可以把 以”.“开始的语句都删除了再阅读。
-
操作数指示符
操作数三种类型:
●立即数,即常数值。
●寄存器,表示寄存器的内容
●存储器引用,它会根据计算出来的地址(有效地址)访问某个存储器位置。
-
对齐
对齐策略为:任何k字节对象的地址都必须是k的倍数;特别地,要求一个double的地址应该是8的倍数。
分配存储器的例程(如malloc)的设计必须使得它们返回的指针能满足最糟糕的对齐限制,通常是4或者是8。
结构体的对齐策略,考虑到分配结构体数组,要求其大小是结构体中最大字节类型的整数倍。
-
存储器的越界引用和缓冲区溢出
!对抗缓冲区溢出攻击
a.栈随机化 b.栈破坏检测 c.限制可执行代码区域
缓冲区溢出的一个更加致命的使用就是让程序执行它本身不愿意执行的函数。
-
浮点运算单元(FPU),IA32浮点寄存器的宽都是80位。浮点运算器的行为可能依赖于值是保存在寄存器中,还是存储器中。
-
综合:理解指针
1)每个指针都对应一个类型
2)每个指针都有一个值
3)指针用&运算符创建
4)操作符用于指针的间接引用
5)数组与指针紧密联系
6)指针也可以指向函数
四、处理器体系结构
-
程序员可见的状态
Y86处理器有八个寄存器:%eax、%edx、%ecx、%ebx、%esi、%edi、%esp和%ebp。每个程序存储器存储一个字。
● 寄存器%esp被入栈、出栈、调用和返回指令作为栈指针。
● 3个一位的条件码:ZF、SF、OF;保存最近的算术或逻辑指令所造成影响的有关信息。
● 程序计数器(PC)存放当前正在执行指令的地址。
● 存储器:保存程序和数据。Y86用虚拟地址引用存储器位置。硬件和操作系统软件联合起来将虚拟地址翻译成实际或物理地址,指明数据实际保存的地方。虚拟存储器提供给Y86程序一个单一的字节数组映像。
● 状态码Stat,表明程序执行的总体状态。
-
Y86指令集
-
逻辑设计和硬件控制语言HCL
1)逻辑门——逻辑门只对单个位的数进行操作。
2)组合电路和HCLB布尔表达式
组合电路——将很多逻辑门组合成一个网,构建计算块。
!两个限制: a.两个或多个逻辑门的输出不能连接在一起,否则它们可能会使线上的信号矛盾,可能会导致一个不合法的电压或电路故障;b.这个网必须是无环的。回路会导致网络计算的函数有歧义。
-
两类存储设备:
1)寄存器(简称寄存器):存储单个位或字,时钟信号控制寄存器加载输入值。
2)随机访问存储器(简称存储器):存储多个字,用地址来选择该杜或该写哪个字。
-
通常,处理一条指令包括很多操作。
1)取指(fetch):取指阶段从存储器读入指令,地址为程序计数器PC的值。从指令中抽取出指令指示符字节的两个四位部分,称为icode(指令代码)和ifun(指令功能)。
2)解码(decode):解码阶段从寄存器文件读入最多两个操作数,得到值valA or/and valB。
3)执行(execute):在执行阶段,算术/逻辑单元(ALU)要么执行指令指明的操作(根据ifun的值),计算存储器引用的有效地址,要么增加或减少栈指针。
4)访存(memory):访存阶段可以将数据写入存储器,或者从存储器读出数据。读出的值为valM。
5)写回(write back):写回阶段最多可以写两个结果到寄存器文件。
6)更新PC(PC update,程序计数器):将PC设置成下一条指令的地址。
[ 指令执行过程,从PC中取出指令,然后沿着上述的几个步骤操作,周而复始的进行循环。]
-
SEQ硬件结构、SEQ的时序
SEQ的实现包括组合逻辑和两种存储器设备:时钟寄存器(程序计数器和条件码寄存器)和随机访问寄存器(寄存器文件、指令存储器和数据存储器)。
!!遵循以下原则组织计算:
处理器从来不需要为了完成一条指令的执行而读由指令更新的状态。
五、存储器层次结构
1.存储技术
-
随机访问存储器(RAM,Random-access memory)分为两类-静态和动态。
静态RAM(SRAM)比动态RAM(DRAM)更快,但也贵很多。SRAM用来作为高速缓存存储器,即可以在CPU芯片上,也可以不在CPU芯片上。DRAM用来作为主存以及图形系统的帧缓冲区。
1)静态RAM
SRAM将每个位存储在一个双稳态(bistable)存储器单元(cell)中。每个单元是用一个六晶体管电路来实现的。这个电路的一个属性是:它可以无限制地保持在两个不同的电压配置(configuration)或状态(state)之一。其他任何状态都是不稳定的。
由于SRAM的双稳态特性,只要有电,它就会永远地保持它的值,即使有干扰,如电子噪音,当干扰消除,电路也能恢复到稳定值。
2)动态RAM
DRAM将每个位存储为对电容的充电。电容约为30×10-15F。
-
磁盘构造(磁盘是由盘片构成的。表面覆盖着磁性记录材料。)
磁盘结构:盘片、磁道、扇区、间隙、柱面;磁盘驱动器
-
内存可以看成字节数组、磁盘可以看成块数组)。现代磁盘构造一个B个扇区大小的逻辑块的序列,编号0—B-1。磁盘中有一个小的硬件/固件设备,成为磁盘控制器。可将一个逻辑块号翻译成一个(盘面、磁道、扇区)三元组。
- 直接存储器访问——设备自己执行读或写总线事务,不需要CPU的干涉。这种数据传送成为DMA传送
2. 局部性
-
局部性原理:一种倾向性,倾向于引用邻近于其他最近引用过的数据项的数据项,或者最近引用过的数据项本身。
局部性有两种不同的形式:时间局部性和空间局部性。
1) 对程序数据引用的局部性————随着步长的增加,空间局部性下降。
2) 取指令的局部性————代码区别于程序的一个重要属性是在运行时他不能被修改。当程序正在执行时,CPU只从存储器中读取他的指令,绝不会重写或修改这些指令。
-
步长为1的引用模式:就是顺序访问一个向量的每个元素,有时也被称为顺序引用模式,它是程序中 空间局部性常见和重要的来源。
3.存储器结构和高速缓存存储器
-
使用高速缓存的过程成为缓存。
!存储器层次结构的中心思想:对于每个K,位于k层的更快更小的存储设备作为位于k+1层的更慢更大的存储设备的缓存。即每层存储设备都是下一层的“缓存”。
数据以块大小为传送单元。一般而言,层次结构中较低层的设备的访问时间较长,倾向于使用较大的快。
-
高速缓存结构可以用元组(S,E,B,m)来描述。高速缓存的大小(容量)C指的是所有块大小的和。标记位和有效位不包括在内。因此C=SEB。
-
高速缓存确定一个请求是否命中,然后抽取出被请求字的过程分为三步:(1)组选择;(2)行匹配;(3)字抽取。
-
存储器山
每台计算机都有表明他存储器系统的能力特色的唯一的存储器山——就是把存储器系统的性能用关于时间和空间局部性的山表示。
● 想要达成的目的:使得程序运行在山峰而不是低谷
● 目标:利用时间局部性,使得频繁使用的字从L1中取出;利用空间局部性,使得尽可能多的字从一个L1高速缓存行中访问到。
六、链接
1.静态链接
-
链接器将重定位目标文件(relocatable object files)组合成一个可执行目标文件。cpp(c previous processor,C预处理器);ccl(C编译器);as(汇编器)
-
为了创建静态链接,链接器完成两个主要任务:
1)符号解析:将每个符号引用和一个符号定义联系起来。
2)重定位:编译器和汇编器生成从0地址开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置,从而重定位这些节。
2.目标文件
目标文件有三种形式:
● 可重定位目标文件
● 可执行目标文件
● 共享目标文件
3.符号解析
C++和Java中能使用重载函数,是因为编译器将每个惟一的方法和参数列表组合编码成一个对链接器来说惟一的名字。这种编码过程叫做毁坏,而相反的过程叫恢复。
C++和Java使用的是兼容的毁坏策略。一个已毁坏类的名字是由名字中字符的整数数量,后面跟原始名字组成的。比如类Foo被编译成3Foo;方法被编译成:原始方法名 + __ + 已毁坏的类名+ 再加上每个类参数的一个字母。如Foo::bar(int, long)被编译成bar__3Fooil。毁坏全局变量和模板名字的策略是相似的。
-
链接器如何解析多处定义的全局符号
函数和已初始化的全局变量是强符号;未初始化的全局变量是弱符号。
Unix链接器使用下面的规则来处理多重定义的符号:
● 规则1: 不允许有多个强符号。 ● 规则2:如果有一个强符号和多个弱符号,那么选择强符号。 ● 规则3:如果有多个弱符号,那么从这些弱符号中任意选择一个。
-
unix中,静态库以一种称为存档(archive)的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合,有一个头部描述每个成员目标文件的大小和位置;后辍名为.a。
-
链接器使用静态库来解析引用的过程
在符号解析时,链接器从左到右按照它们在编译器驱动程序命令行上出现的顺序来扫描可重定位目标文件和存档文件。在扫描中,链接器维持一个可重定位目标文件的集合E,这个集合中的文件会被合并起来形成可执行文件,和一个未解析(引用了但是尚未定义的符号)的符号集U,以及一个在前面输入文件中已定义的符号集合D。初始化,E,U,D都是空的。
4.可执行目标文件
-
ELF(Executable and Linkable Format)
代码段的地址总是比数据段的地址小。
-
加载可执行目标文件
任何Unix程序都可以通过调用execve函数来调用加载器。加载器将可执行目标文件中的代码和数据从磁盘拷贝到存储器中,然后通过跳转到程序的第1条指令,即入口点,来运行该程序。将程序拷贝到存储器并运行的过程叫做加载。
每个Unix程序都有一个运行时存储器映像。
5.动态链接共享库
-
共享库是一个目标模块,在运行时,可以加载到任意的地存储器地址,并在存储器中和一个程序链接起来。这个过程称为动态链接(dynamic linking),是由一个叫做动态链接器(dynamic linker)的程序执行的。
-
共享库也称为共享目标(shared object),在Unix系统中通常用.so后缀来表示。(在MS OS 中为DLL文件) 注意:静态链接与动态链接的区别:静态链接是把程序所需要的库代码和数据拷贝和嵌入到引用它们的可执行文件中;而动态链接是所有引用该库的可执行文件文件共享这个.so(dll)文件中的代码和数据。
-
从应用程序中加载和链接共享库 通过几个函数,dlopen加载和链接共享库,dlsym通过输入的共享库的符号名字,返回符号的地址;dlclose卸载共享库,dlerror返回前面函数执行情况的一个字符串。
6.PIC(与位置无关的代码,Position-independent code)
-
共享库的一个主要目的就是允许多个正在运行的进程共享存储器中相同的库代码,因而节约存储器资源。
-
PIC:编译库代码,不需要链接器修改库代码,就可以在任何地址加载和执行这些代码。
在一个IA32系统中,对同一个目标模块中过程的调用不需要特殊处理的,因为引用是PC相关的,已知偏移量,就是PIC了。然而,对外部定义的过程调用和对全局变量的引用通常不是PIC,因为它们都要求在链接时重定位。
-
如何对全局变量生成PIC引用呢? 基于以下事实:无论我们在存储器中的何处加载一个目标模块(包括共享模块),数据段总是分配为紧随在代码段后面。因此,代码段中任何指令和数据段中任何变量之间的距离都是一个运行时常量。
7.处理目标文件的工具
GNU binutils包。如objdump,ar,ldd。
8.小结:
链接可以在编译时由静态编译器完成,也可以加载和运行时由动态编译器完成。链接器处理称为目标文件的二进制文件,它有三种不同的形式:可重定位的,可执行的,和共享的。可重定位的目标文件由静态链接器组合成一个可执行的目标文件,它可以加载到存储器中并执行。共享目标文件(共享库)是在运行时由动态链接器链接和加载的,或者隐含地在调用程序被加载和开始执行时,或者根据需要在程序中调用dlopen库的函数时。