深入理解计算机系统
发布时间:2025-11-21 15:25:11

深入理解计算机系统本文简介:1.在Unix系统上,从源文件到目标文件的转化是由编译器驱动程序完成的:unix>gcc–ohellohello.c这个翻译过程可分为四个阶段完成(预处理器、编译器、汇编器和链接器),这四个阶段的程序一起构成了编译系统。预处理阶段:预处理器根据以字符#开头的命令,修改原始的C程序。比如hello.c

深入理解计算机系统本文内容:

1.

在Unix系统上,从源文件到目标文件的转化是由编译器驱动程序完成的:

unix>

gcc

–o

hello

hello.c

这个翻译过程可分为四个阶段完成(预处理器、编译器、汇编器和链接器),这四个阶段的程序一起构成了编译系统。

预处理阶段:

预处理器根据以字符#开头的命令,修改原始的C程序。比如hello.c中第一行的#include命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入到程序文本中。结果就得到了另一个C程序,通常是以.i作为文件扩展名。

编译阶段:编译器将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。汇编语言程序中的每条语句都以一种标准的文本格式确切的描述了一个低级机器语言指令。汇编语言是非常有用的,因为它为不同高级语言的不同编译器提供了通用的输出语言。例如,C编译器和Fortran编译器产生的输出文件用的都是一样的汇编语言。

汇编阶段:接下来,汇编器将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中。hello.o文件是一个二进制文件,它的字节编码是机器语言指令而不是字符。

链接阶段:

hello程序调用了printf函数,它是每个C编译器都会提供的标准C库中的一个函数。printf函数存在于一个名为printf.o的单独的预编译好了的目标文件中,而这个问题件必须以某种方式合并到我们的hello.o程序胡总。链接器就负责处理这种合并。结果就得到hello文件,它是一个可执行目标文件,可以被加载到内存中,由系统执行。

2.

每个I/O设备都通过一个控制器或适配器与I/O总线相连。控制器和适配器之间的区别主要在于它们的封装方式。控制器是置于I/O设备本身的或者系统的主印制电路板(主板)上的芯片组,而适配器则是一块插在主板插槽上的卡。无论如何,它们的功能都是在I/O总线和设备之间传递信息。

3.

主存(DRAM)

高速缓存(SRAM)

4.

文件时对I/O设备的抽象表示,虚拟存储器是对主存和磁盘I/O设备的抽象表示,进程则是对处理器、主存和I/O设备的抽象表示。

5.

字:每台计算机都有一个字长,指明整数和指针数据的标称大小(nominal

size)。因为虚拟地址是以这样的一个字来编码的,所以字长决定的最重要的系统参数就是虚拟地址空间的最大大小。

6.

对于跨越多字节的程序对象,我们必须建立两个规则:这个对象的地址是什么,以及在存储器中如何排列这些字节。在几乎所有的机器上,多字节对象都被存储在连续的字节序列,对象的地址为所使用字节中最小的地址。

7.

右移操作:一般而言,机器支持两种形式的右移:逻辑右移和算术右移。逻辑右移在左端补K个0.

算术右移是在左端补k个最高有效位的值。

8.

C语言标准并没有明确定义应该使用哪种类型的右移。对于无符号数据(也就是以限定词unsigned声明的整型对象),右移必须是逻辑的。而对于有符号数据(默认的声明的整型对象),算术的或者逻辑的右移都可以。不幸的是,这就意味着任何假设一种或者另一种右移形式的代码都潜在着可移植性的问题。然而实际上,几乎所有的编译器/机器组合都对有符号数据使用算术右移,且许多程序员也都假设机器会使用这种右移。另一方面,Java对于如何进行右移有明确的定义。表达式x>>k会将x算术右移k个位置,而x>>>k会对x做逻辑右移。

9.

对于一个由w位组成的数据类型,如果要移动k>=w位会得到什么结果呢?例如,在一个32位机器上计算下面的表达式会得到什么结果呢?C语言标准很小心地规避了说明在这种情况下该如何做。在许多机器上,当移动一个w位的值时,移位指令只考虑位移量的低log2w位,因此实际上位移量就是通过计算k

mod

w得到的。不过这种行为对于C程序来说是没有保证的,所以移位数量应该保持小于字长。

另一方面,Java特别要求位移数量应该按照我们前面所讲的求模的方法来计算。

10.

C和C++都支持有符号(默认)和无符号数。Java只支持有符号数。

11.

由于C语言对同时包含有符号和无符号数表达式的这种处理方式,出现了一些奇特的行为。当执行一个运算时,如果它的一个运算数是有符号的而另一个是有符号的,那么C语言会隐式地将有符号参数强制类型转换为无符号数,并假设这两个数都是非负的,来执行这个运算。

12.

补码非的两种计算方法:1)对每一位求补,再对结果加1;

2)最右边的1不变,其左边的所有位取反。

13.

IA32是”Intel

32位体系结构”(Intel

Architecture

32-bit),以及最新的Intel***,即IA32的***位扩展,我们也称为X86-***.

我们最常用的名字是“X86”,用它指代整个系列,也反映了直到i486处理器命名的惯例。

14.

8086:是一个16位的微处理器。而i386才将体系结构扩展到了32位。

15.

假设一个C程序,有两个文件p1.c和p2.c。我们在一台IA32机器上,用Unix命令行编译这些代码如下:

unix>

gcc

–O1

–o

p

p1.c

p2.c

编译选项-O1告诉编译器使用第一级优化。通常,提高优化级别会使最终程序运行得更快,但是编译时间可能会变长,用调试工具对代码进行调试会更困难。正如我们还会看到的,使用更高级别的优化产生的代码会严重改变形式,以至于产生的机器代码和初始源代码之间的关系非常难以理解。我们使用第一级优化作为学习工具。实际中,从得到的程序性能方面考虑,第二级优化(-O2)被认为是较好的选择。

实际上gcc命令调用了一系列的程序,将源代码转化成可执行代码。首先,c预处理器扩展源代码,插入所有用#include命令指定的文件,并扩展所有用#define声明指定的宏。然后,编译器产生两个源代码的汇编代码,名字分别为p1.s和p2.s。接下来,汇编器将汇编代码转化为二进制目标代码文件名为p1.o和p2.o。目标代码是机器代码的一种形式,它包含所有指令的二进制表示,但是还没有填入地址的全局值。最后链接器将两个目标代码文件与实现库函数(例如printf)的代码合并,并产生最终的可执行代码文件p.

16.

由于是从16位体系结构扩展成32位的,Intel用术语“字”(word)表示16位数据类型。因此,称32位数为“双字”(double

words),称***位数为“四字”(quad

words)。

17.

各种不同的操作数的可能性被分为三种类型。第一种类型是立即数(immediate),也就是常数值。在ATT格式的汇编代码中,立即数的书写方式为‘$’后面跟一个用标准C表示法表示的整数,比如,$-577或$0x1F。任何能放进一个32位的字里的数值都可以用作立即数,不过汇编器在可能时会使用一个或两个字节的编码。第二种类型是寄存器(register),它表示某个寄存器的内容,对双字操作来说,可以是8个32位寄存器中的一个(%eax),对字操作来说,可以是8个16位寄存器中的一个(%ax),或者对于字节操作来说,可以是8个单字节寄存器元素中的一个(%a1)。第三类操作数是存储器(memory)引用,它会根据计算出来的地址(通常称为有效地址)访问某个存储器位置。

18.

MOV类中的指令将源操作数的值复制到目的操作数中个。源操作数指定的值是一个立即数,存储在寄存器中或者存储器中。目的操作数指定一个位置,要么是一个寄存器,要么是一个存储器地址。IA32加了一条限制,传送指令的两个操作数不能都指向存储器位置,将一个值从一个存储器位置复制到另一个存储器位置需要两条指令——第一条指令将源值加载到寄存器中,第二条将寄存器值写入目的位置。

19.

一个过程调用包括将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分。另外,它还必须在进入时为过程的局部变量分配空间,并在退出时释放这些空间,并在退出时释放这些空间。大多数机器,包括IA32,只提供转移控制到过程和从过程中转移出控制这种简单的指令。数据传递、局部变量的分配和释放通过操纵程序栈来实现。

20.

对抗缓冲区溢出攻击:1)栈随机化,2)栈破坏检测。其思想是设置一个金丝雀值,用来做校验用。3)限制可执行代码区域

21.

X86-***硬件和编程规则的形成改变了处理器,过去它严重依赖于栈来保存程序状态,现在则是将最常用的状态部分保存在更快并扩展了的寄存器组中。

22.

RISC与CISC:

CISC指令数量很多。IA32就是CISC.指令编码是可变长度的,指令操作数的方式很多样,可以对存储器和寄存器操作数进行算术和逻辑运算,有条件码,可以用于条件分支检测;而RISC指令数量少的多,编码是固定长度的,只能对寄存器操作数进行算术和逻辑运算,没有条件码。

23.

两段代码:

void

twiddle1(intxp,intyp)

void

twiddle2(intxp,intyp)

{

{xp

+=yp;xp

+=

2yp;xp

+=yp;

}

}

后者效率更好。前者需要六次存储器引用(2次读*xp,2次读*yp,2次写*xp),而后者只需要3次(读*xp,读*yp,写*xp)。不过这两段代码在xp与yp都指向同一个存储器位置(存储器别名引用)的时候会有不同的结果,前者是4倍的*xp,后者只是3倍的*xp。

24.

SRAM与DRAM对比:

每位晶体管数

相对访问时间

持续的?

敏感的?

相对花费

应用

SRAM

6

100×

高速缓存存储器

DRAM

1

10×

主存,帧缓冲区

25.

ROM:存储在ROM设备中的程序通常称为固件(firmware)。比如PC的BIOS。

26.

局部性原理:局部性通常有两种不同的形式,时间局部性和空间局部性。在一个具有良好时间局部性的程序中,被引用过一次的存储器位置很可能在不远的将来再被多次应用。在一个具有良好空间局部性的程序中,如果一个存储器位置被引用了一次,那么程序很可能在不远的将来引用附近的一个存储器位置。

27.

量化评价一个程序中局部性的简单原则:

重复引用同一个变量的程序有良好的时间局部性

对于具有步长为k的引用模式的程序,步长越小,空间局部性越好。

对于取指令来说,循环有好的时间和空间局部性。

深入理解计算机系统

免责声明:本站内容(文字信息+图片素材)来源于互联网公开数据整理或转载,仅用于学习参考,如有侵权问题,请及时联系本站删除,我们将在5个工作日内处理。

联系邮箱:chuangshanghai#QQ.COM(把#换成@)

Copyright © 卖艺吧 版权所有