计算机系统基本组成与功能
1946年2月,第一台通用电子计算机ENIACE(lectronic Nurerical Integrator And Corputer电子数字积分计算机)诞生,使用十进制,采用手动编程,通过设置开关和插拔电缆来实现
1945年冯●诺伊曼发表全新的存储程序通用电子计算机方案
冯●诺伊曼结构最重要的思想是存储程序Stored-program,即把程序存储在计算机内由计算机自动存取指令并执行
内部以二进制表示指令和数据,每条指令由操作码和地址码两部分组成。操作码指出操作类型,地址码指出操作数的地址,由一串指令组成程序
冯 ● 诺依曼型计算机基本工作方式:
- 控制器将计算所需的原始数据和计算步骤的程序指令,通过输入设备送入计算机存储器
- 控制器向存储器发送取指指令(主要功能是从存储器中读取出指令),存储器中的程序指令送入控制器
- 控制器对取出的指令进行译码,然后向存储器发送取数指令,存储器的相关运算数据送入运算器
- 控制器向运算器发送运算指令,运算器执行计算得到结果,并把运算结果存入存储器中
- 最后控制器向输出设备发送输出指令,输出设备输出计算机结果
现代计算机结构模型
- CPU:中央处理器
- PC:程序计数器
- MAR:存储器地址寄存器
- ALU:算术逻辑部件
- IR:指令寄存器
- MDR:存储器数据寄存器
- GPRs:通用寄存器组(由若干通用寄存器组成,早期就是累加器)
详细工作步骤
- 指令获取:当CPU需要执行新的指令时,它会从主存储器(Main Memory)中获取指令。这一过程由内存地址寄存器(MAR)和内存地址控制器(Memory Address Controller)协同完成。MAR存储即将从主存中读取或写入的单元的地址,而内存地址控制器则负责管理数据在主存中的流动
- PC更新:程序计数器(PC)会更新,指向下一条要执行的指令的地址。PC是CPU中的一个寄存器,用于存储当前执行的指令的地址
- 指令解码:指令被送入指令寄存器(IR)进行解码。指令寄存器是一个暂存器,用于存储从主存中读取的指令。指令译码器(Instruction Decoder)对指令进行解码,以确定需要执行的操作和操作数
- 执行指令:一旦指令被成功解码,算术逻辑单元(ALU)开始工作。ALU是CPU的核心部分,负责执行算术和逻辑运算。根据解码的指令,ALU可能从主存中读取数据,进行计算,然后将结果写回主存
- 数据流动:在执行指令的过程中,数据在各部件之间流动。数据从主存流入ALU进行计算,然后再流回主存进行存储或进一步的处理。输入设备(如键盘、鼠标)提供原始数据,而输出设备(如显示器、打印机)则显示或输出处理后的数据
- 数据存储:当需要存储数据时,数据会从ALU或其他部件流入存储器数据寄存器(MDR),然后被写入主存或某个特定的存储器位置
- 通用寄存器组:通用寄存器组(GPRs)由若干通用寄存器组成,早期就是累加器。这些寄存器用于暂存数据和地址,以便快速访问。CPU可以在这些寄存器之间移动数据,进行计算,或与主存进行交互
- 控制流:中央处理器(CPU)的控制器负责协调以上所有步骤。控制器通过读取指令并在ALU上执行相应的操作来控制数据流和计算过程
计算机的指令和数据
程序启动前,指令和数据都存放在存储器中,形式上没有差别, 都是01序列
程序由指令组成,程序被启动后,计算机能自动取出一条一条 指令执行,在执行过程中无需人的干预
指令执行过程中,指令和数据被从存储器取到CPU,存放在CPU 内的寄存器中,指令在IR中,数据在GPR中
指令中需给出的信息:
-
操作性质(操作码)
-
源操作数1 或/和 源操作数2 (立即数、寄存器编号、存储地址)
-
立即数:指在指令中直接给出的操作数
-
寄存器编号:计算机中的寄存器用于存储数据和指令,每个寄存器都有一个唯一的编号
-
存储地址:存储器中的每个单元都有一个唯一的地址,通过该地址可以访问存储器中的数据
-
-
目的操作数地址 (寄存器编号、存储地址)
计算机的基本部件及功能:
- 运算器(数据运算):ALU、GPRs、标志寄存器等
- 存储器(数据存储):存储阵列、地址译码器、读写控制电路
- 总线(数据传送):数据(MDR)、地址(MAR)和控制线
- 控制器(控制):对指令译码生成控制信号
什么是计算机:
计算机是一种能对数字化信息进行自动、高速算术和逻辑运算的处理装置
程序开发和执行
- 高级语言中一条语句对应几条、几十条甚至几百条指令
- 有面向过程和面向对象的语言之分
- 处理逻辑分为三种结构 : 顺序结构、选择结构、循环结构
编译程序(Complien):将高级语言源程序转换为机器级目标程序,执行时只要启动目标程序即可
解释程序(nterpreter):将高级语言语句逐条翻译成机器指令并立即执行,不生成目标文件
计算机只能理解机器语言01,最终还是会将高级语言转换为机器语言程序
数据经常在各存储部件间传送。故现代计算机大多采用缓存技术
所有过程都是在CPU执行指令所产生的控制信号的作用下进行的
不同层次语言之间的等价转换
高级语言开发需要复杂的支持环境
程序执行结果不仅取决于算法、程序编写,而且取决于语言处理系统、操作系统、ISA指令集体系结构
ISA处于软件和硬件的交界面(接口),ISA是对硬件的抽象,所有软件功能都建立在ISA之上,ISA是最重要的层次
ISA指Instruction Set Architecture指令集体系结构,有时简称为指令系统
ISA是一种规约(Specification),它规定了如何使用硬件
- 可执行的指令的集合,包括指令格式、操作种类以及每种操作对应的操作数的相应规定
- 指令可以接受的操作数的类型
- 操作数所能存放的寄存器组的结构,包括每个寄存器的名称、编号、
长度和用途 - 操作数所能存放的存储空间的大小和编址方式
- 操作数在存储空间存放时按照大端还是小端方式存放
- 指令获取操作数的方式,即寻址方式
- 指令执行过程的控制方式,包括程序计数器(PC)、条件码示义等
当然,ISA是系统程序员需要深入理解的层次,应用程序员工作在由语言处理系统(主要是编译器和汇编器)的抽象层
数据的表示和存储
机器数:用0和1编码的计算机内部的0/1序列
数值数据表示的三要素:
-
进位计数制
十进制、二进制、十六进制、八进制数及其相互转换
-
定、浮点表示(解决小数点)
- 定点整数、定点小数
- 浮点数(可用一个定点小数和一个定点整数来表示)
-
如何用二进制编码(解决正负号)
原码、补码、反码、移码(反码很少用)
在R进制数字系统中,应采用R个基本符号(0,1,2,...,R-1) 表示各位上的数字,采用“逢R进一”的运算规则,对于每一个数位 i, 该位上的权为Ri。R被称为该数字系统的基
- 二进制:R=2,基本符号为0和1
- 八进制:R=8,基本符号为0,1,2,3,4,5,6,7
- 十进制:R=10,基本符号为0,1,2,3,4,5,6,7,8,9
- 十六进制:R=16,基本符号为0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
八进制octal用后缀O表示,十六进制用后缀H或前缀0X表示,现代计算机系统多用十六进制表示机器数
R进制数转换为十进制数:按权展开
(10101.01)2=1x24+1x22+1x20+1x2-2=(21.25)10
a 的 -b 次方,实际上是 1 除以 (a 的 b 次方)
(307.6)8=3x82+7x80+6x8-1=(199.75)10
(3A. 1)16=3x161+10x160+1x16-1=(58.0625)10
十进制数转换为二进制,二进制转换为十六进制或八进制
十进制转八进制
整数:除基取余,下高上低
小数:乘基取整,下底上高
可能小数部分总得不到0,此时得到一个近似值,现实中的精确值可能在机器内部无法用0和1精确表示
数值数据中的小数点在计算机中只能通过约定小数点的位置来表示
- 小数点位置约定在固定位置的数称为定点数
- 小数点位置约定为可浮动的数称为浮点数
定点小数用来表示浮点数的尾数部分
定点整数用来表示整数(带符号整数和无符号整数)
任何实数:X=(-1)s ×M×RE
- S取值为0或1,用来决定数X的符号
- M是一个二进制定点小数 ,称为数X的尾数
- E是一个二进制定点整数,称为数X的阶或指数
- R是基数,可以为2、4和16等
- 计算机中只要表示S、M和E三个信息,就能确定X的值,这就是浮点数
数值数据的表示
原码
-
正号首位为0
-
负号首位为1
-
数值部分不变
- 容易理解, 但是:
- 0的表示不唯一,故不利于程序员编程
- 加、减运算方式不统一
- 需额外对符号位进行处理,不利于硬件设计
- 容易理解, 但是:
-
从50年代开始,整数都采用补码来表示,但浮点数的尾数用原码定点小数表示
移码
也被称为增码或偏置码,通常用于表示浮点数的阶码(指数)
将一个数值增加一个偏移常数(Excess/bias)来定义,当编码位数为n时,bias取2n-1或2n-1-1
当n=4,Ebiased=E+23
-8(+8)=0000B(移码表示)
0(+8)=1000B(移码表示)
0的移码表示是唯一的1000,当bias为2n-1时,移码和补码仅第一位不同
补码
在一个模运算系统中,一个数与它除以模后的余数等价
时钟是一个模12系统:10 - 4=10 + 8(mod12),- 4=8
- 一个负数的补码等于模减该负数的绝对值,12 - 4 = 8
- 对于某一确定的模,某数减去小于模的另一数,总可以用该数加上另一数负数的补码来代替
计算机中的运算器是模运算系统
0111 1111 - 0100 0000
= 0111 1111 + (28- 0100 0000)
= 0111 1111 + 1100 0000
= 1 0011 1111 (mod 28)
= 0011 1111
只留余数,1被丢弃
一个负数的补码等于将对应正数补码,各位取反、末位加一
补码的定义
假定补码有n位,则[X]补= 2n + X (-2n-1 ≤ X<2n-1 ,mod 2n) X是真值,[x]补是机器数
- 正数符号位为0,数值部分不变
- 负数符号位为1,数值部分各位取反,末位加1
- 变形补码:双符号,用于存放可能溢出的中间结果
- 8位无符号整数最大是255(1111 1111)
- 8位带符号整数最大为127(0111 1111)
三种定点编码方式
- 原码:定点小数,用来表示浮点数的尾数
- 移码:定点整数,用于表示浮点数的阶(指数)
- 补码:50年代以来,所有计算机都用补码来表示带符号整数
- 补码运算系统是模运算系统,加、减运算统一
- 数0的表示唯一,方便使用
- 比原码多表示一个最小负数
非数值数据的编码表示
表示逻辑(关系)表达式中的逻辑值时用到逻辑数据
- 表示:用1位表示,N位二进制数可表示N个逻辑数据
- 运算:按位进行,|、&、^、<<、>>等
- 识别:逻辑数据和数值数据在形式上并无差别,都是一串0/1序列, 计算机靠指令来识别
ASCLL码
-
A 65
-
a =65+32=97
记住首字母就能推导其他32个字母在ASCLL码表中位置
汉字编码
-
交换码
交换码就是国标码,是计算机及其他设备之间交换信息的统一标准
国标码也有一张码表,表中任意汉字或符号的区号和位号的组合,叫做这个汉字或符号的区位码
机内码是计算机汉字系统中使用的二进制字符编码
转换方式是先转换为十六进制
- 国标码=区位码+2020H
- 机内码=国标码+8080H
- 机内码=区位码+A0A0H
- 八进制是O,十进制是D,十六进制是H
-
外码
外码是汉字的输入编码
-
内码
内码是汉字的内部编码
内码=国标码+8080H
-
汉字字形码
显示器或打印机输出汉字使用的代码
-
汉字地址码
是字库中存储的每个汉字的逻辑地址
需要2字节才能表示一个汉字内码是因为汉字总数超过6万字,216=65536
数据宽度和存储容量的单位
除比特和字节外,也经常使用字Word为单位
字和字长概念不同
- 字长指数据通路(CPU内部数据流经的路径以及路径上的部件)的宽度,等于CPU内部总线的宽度、运算器的位数、通用寄存器的宽度(这些部件的宽度都是一致的)
- 字表示被处理信息的单位,用来度量数据类型的宽度
容量单位1KB是210字节=1024B,1MB=220字节=1024KB
速度单位1Kbps是103/s=1000bps,1Mbps=106/s=1000Kbps
不同机器上表示的同一种类型的数据可能宽度不同,必须确定相应的机器级数据表示方式和相应的处理指令
同类型数据并不是所有机器都采用相同的宽度,分配的字节数随ISA、机器字长和编译器的不同而不同
- 整数乘法运算比移位和加法等运算所用时间长,通常一次乘法运算需要多个时钟周期,而一次移位、加法和减法等运算只要一个或更少的时钟周期,因此编译器在处理变量与常数相乘时,往往以移位、加法和减法的组合运算来代替乘法运算
- 对于整数除法运算,由于计算机中除法运算比较复杂,而且不能 用流水线方式实现,所以一次除法运算大致需要30个或更多个时 钟周期,比乘法指令的时间还要长,为了缩短除法运算的时间,编译器在处理一个变量与一个2的幂次形式的整数相除时,常采用右移运算来实现
链接概述和目标文件格式
预处理
处理源文件中以#开头的预编译指令
- 删除#define并展开所定义的宏
- 处理所有条件预编译指令
- 插入头文件到#include
- 删除所有的注释
- 添加行号和文件名标识,以便编译时编译器产生调试用的行号信息
经过预编译处理后,得到的是预处理文件
编译
编译过程就是将预处理文件进行词法分析、语法分析、语义分析、优化后,生成汇编代码文件
用来进行编译处理的程序称为编译程序(编译器Compiler)
经过编译后得到汇编代码文件
汇编
汇编器将汇编语言源程序转换为机器指令序列
汇编指令和机器指令一一对应,前者是后者的符号表示,它们都属于机器级指令,所构成的程序称为机器级代码
汇编后得到可重定向目标文件
链接
链接过程是将多个可重定位目标文件合并以生成可执行目标文件
优点:
-
模块化
- 一个程序可以分成很多源程序文件
- 可构建公共函数库,如数学库,标准C库等
-
效率高
-
时间上可分开编译
只需重新编译被修改的源程序文件,然后重新链接
-
空间上无需包含共享库所有代码
源文件中无需包含共享库函数的源码,只要直接调用即可
可执行文件和运行时的内存中只需包含所调用函数的代码 而不需要包含整个共享库
-
三类目标文件
-
可重定位目标文件 (.o)
其代码和数据可以和其他可重定位文件合并为可执行文件
每个.o文件由对应的.c文件生成
每个.o文件代码和数据地址都从0开始
-
可执行目标文件 (Linux默认a.out,Windows*.exe)
包含的代码和数据可以被直接复制到内存并被执行
代码和数据地址为虚拟地址空间中的地址
-
共享的目标文件 (Linux中的*.so)
特殊的可重定位目标文件,能在装入或运行时被装入到内存并自动被链接,称为共享库文件
Windows中称其为Dynamic Link Libraries,DLL
目标代码Object Code指编译器和汇编器处理源代码后所生成的机器语言目标代码
目标文件Object File指包含目标代码的文件
符号及符号表
符号解析
程序中有定义和引用的符号
- 编译器将定义的符号存放在一个符号表节中
- 符号表是一个结构数组
- 每个表项包含符号名、长度和位置等信息
- 编译器将符号的引用存放在重定向节中
- 链接器将每个符号的引用都与一个确定的符号定义建立关联
重定位
- 将多个代码段与数据段分别合并为一个单独的代码段和数据段
- 计算每个定义的符号在虚拟地址空间中的绝对地址
- 将可执行文件中符号引用处的地址修改为重定位后的地址信息
三种链接器符号
每个可重定位目标模块m都有一个符号表,它包含了在m中定义和引用的 符号
-
Global symbols(模块内部定义的全局符号)
由模块m定义并能被其他模块引用的符号
-
External symbols(外部定义的全局符号)
由其他模块定义并被模块m引用的全局符号
-
Local symbols(本模块的局部符号)
仅由模块m定义和引用的本地符号
链接器局部符号不是指程序中的局部变量(分配在栈中的临时性变量),链接器不关心这种局部变量
全局符号的强/弱特性
函数名和已初始化的全局变量名是强符号
未初始化的全局变量名是弱符号
链接器对符号的解析规则
- 强符号只能被定义一次,否则链接错误
- 若一个符号被定义为一次强符号和多次弱符号,则按强定义为准,对弱符号的引用被解析为其强定义符号
- 若有多个弱符号定义,则任选其中一个
符号解析时只能有一个确定的定义(即每个符号仅占一处存储空间)
advice:
- 尽量避免使用全局变量
- 一定需要用的话,就按以下规则使用
- 尽量使用本地变量
static
- 全局变量要赋初值
- 外部全局变量要使用
extern
- 尽量使用本地变量
cation:
多重定义全局变量会造成一些意想不到的错误,而且是默默发生的,编译系统不会警告,并会在程序执行很久后才能表现出来, 且远离错误引发处。特别是在一个具有几百个模块的大型软件中, 这类错误很难修正
不想深入了解链接器工作原理,就应该养成良好的编程习惯
重定位
符号解析完成后进行重定位
-
合并相同的节
例如所有.text节合并作为可执行文件中的.text节
-
对定义符号进行重定位(确定地址)
确定新节中所有定义符号在虚拟地址空间中的地址
例如为函数确定首地址,进而确定每条指令的地址,为变量确定首地址
完成这一步后,每条指令和每个全局或局部变量都可确定地址
-
对引用符号进行重定位(确定地址)