关于计算机的最小存储单位
有一篇帖子介绍的非常好
为什么计算机最小的存储单位是字节?而最小到的传输单位是bit?
我来总结一下,计算机中所有的一切确实都是以2进制存储的,但是在实际数据存储中却是以字节为单位的,因为1个位只有两个状态,并不能表示现实生活中一个完整的信息。
比如ASCII码的一个字符小写字母a,这是一个相对人类有用的信息
它在计算机中是以是8个bit存储的,也就是1个字节,1个位的0和1两个状态并不能表示字符a(ASCII码表对应的二进制为0110 0001)
所以人们在设计计算机存储的时候还是要以字节为单位表示一个对人类相对完整的信息
一个位,也就是一个bit,是表示信息的最小单位。只有0和1两种状态,对应计算机电路中的高电平和低电平。在这个维度上,只能表示
一些简单的信息,也可以当做flag来使用。
ps:顺便喷一下这篇帖子中的评论PhoenixJ的评论:
一派胡言。 知道标识位么? 1bit是可以表示信息的。
这傻吊明显没有仔细阅读帖子,博主说的是用字节表示一个相对有用的信息,你都没有好好读人家帖子上来就喷什么一派胡言,恩,那我站在道德的角度上喷你是个loser你高兴?
我一直相信程序员是用自己的技术使人们和自己的生活变得更加美好,这也就要求从业者自己首先要具备一定的素质,你连最基本的尊重别人都做不到还写你mmp的代码~
关于存储器
存储器是按字节编址的
这里的存储器就是指我们的内存(内存条)
广义上的存储器还有硬盘以及别的移动存储设备
咱们所有的数据、代码、指令最终都会转变为二进制存储在内存中
即使用汇编语言写出来的指令最终也都是存储在内存中的一堆堆二进制
存储单元
如上图所示
8086的地址总线是20位的
在二进制下是00000000000000000000 - 11111111111111111111
00000000000000000000 对应的十进制是0
11111111111111111111 对应的十进制是1048575
在16进制下是00000H(0x00000) - FFFFFH(0xFFFFF)
00000H 对应的十进制是0
FFFFFH 对应的十进制是1048575
也就是说地址总线总共可以寻找到1048576个字节(注意,这里是字节!!!!!!!!)
等于1024KB个字节,也就是1MB个字节
也就是说地址总线只能寻找到1048576个字节,1M字节的内存,你即使插上个8G的内存也并没有什么卵用
ps:话说回来以后一定要注意大B和小b,不注意细节的话要向我一样找资料找好久。。。
数据存储是以字节为单位的
那么一个字节就表示一个存储单元
也就是说物理地址00000H - 00001H之间的就是一个存储单元
存储单元中的数据存储
大部分数据存储是以字节为单位表示的
也就是说这种数据占一个存储单元
也有的数据是以字为单位表示的,这样的数据在存储器中占有连续的两个存储单元
比如一个以字为单位的数据1234H,也就是两个字节16位,它存储的时候会占用两个连续的数据单元00000H和00001H。
计算机的运算存储规则如下:
低位字节存入低地址,也就是34H存入00000H
高位字节存入高地址,也就是12H存入00001H
还有的数据是以双字位单位表示的,这样的数据数据在存储器中占有连续的两个字,也就是连续的四个存储单元
以字节为单位的数据就是用它本身的地址表示
而以字为单位的数据则是用它的低位地址表示
也就是说同一个地址,既可以看做字节单元地址,也可以看做字单元地址
逻辑地址与物理地址
物理地址是存储器的绝对地址(在8086中为20位的实际地址)
范围用16进制表示为00000H - FFFFFH
是由CPU访问存储器的时候由地址总线发出的地址
逻辑地址是由段基址和段内偏移地址组成的地址
段基址和段内偏移地址都是16位的无符号二进制数,在程序设计的时候使用
- 比如一个段基址的物理地址是00000H(5位16进制为20位二进制)因为CPU中的寄存器是16位的,也就是说只可以装下16位的地址,那么便舍弃最后一位(在8086中段基址的16进制最后一位都是0)
就变成了0000H(4位16进制为16位二进制)- 段内偏移地址不存在去掉位数的情况,它本身就是一个16进制的数,是址相对于当前的段基址的偏移地址。
注意偏移地址本身就是16位的!!!
物理地址的补充
寻址空间是按照处理器的地址线个数定的,因为8086的地址线只有20根,因此它的寻址能力只有2^20字节 = 1MB。i386结构的处理器都是可以按照字节编址,每个内存单元的地址,不称为物理地址而是线性地址,线性地址通过CPU内存管理单元(MMU)来进行转换,因为在8086上只有段管理机制,因此此时线性地址等价于物理地址。
到32处理器,cpu地址线拥有32根,寻址能力达到4GB,而P4处理器的地址线则拥有35根,可以寻址更大的空间。但是实际内存达不到CPU的寻址空间大小,此时CPU的MMU就需要对线性地址进行向物理地址的转化,此时线性地址就和物理地址不一样了。
决定一个内存单元的物理地址时需要根据当前的内存管理方式进行计算,首先根据虚拟地址计算得到线性地址,然后根据分页机制是否打开,如果没有使用分页机制,线性地址就是物理地址,如果打开分页机制则根据页目录和页表项来计算得物理地址
ps:摘抄自网络
在8086中咱说的物理地址实际上叫线性地址,但是为了简化流程,下文将线性地址以物理地址代替
内存分段
这里所说的都是逻辑地址哦~
也可以说讲的是逻辑地址是怎么由来的
8086CPU外部地址总线是20位的,最大可寻址内存空间为1MB。而8086的寄存器都是16位的。
用16位的地址寻址1MB空间是不可能的。所以就要把内存分段。
分为几个段基址,段基址下又有段内偏移地址
- CPU将内存分为四个大段:
设置4个16位的段寄存器,用于管理4种段。
CS是代码段,DS是数据段,SS是堆栈段,ES是附加段。
把内存分段后,每一个段就有一个段基址。
因为段寄存器都是16位的,所以它们保存的是这个段基址的高16位(段基址的物理地址是20位),这个16位的地址左移四位(后面加上4个0)就可推回20位的段基址物理地址,这是CPU中的地址加法器的工作,下面讲CPU的时候会讲到。- 每个段内又有段内偏移地址,段内偏移地址最大为64KB。
ps:段内偏移地址最大为64K字节,因为寄存器是16位的,2的16次方为65536,每个表示一个字节,也就是65536个字节,除1024,也就是64KB个字节
其实如果段内偏移地址最大是64KB的话,那么1MB字节的内存可以分为16个段,16*64KB = 1MB。
但是这是我们根据具体情况划分的。
王爽的《汇编语言》(第三版 )中说内存并没有被分成一个一个段,段的划分来自于CPU
所以我们只需要记住如下总结就好了:
- 段的划分来自CPU。
- 内存从大体上分为四个段,每个段的物理地址是20位的,但是编写程序的时候一般使用该段的逻辑地址,也就是将最后一位16进制的0舍弃,得到一个4位的16进制地址。
- 每个段内偏移地址最大为64KB(因为16位寄存器只能存放64KB的地址)
物理地址的计算方法
把逻辑段地址左移4位二进制,然后再加上逻辑段内偏移偏移地址,就可以寻找到当前要执行的指令(或是当前要读写的数据)所在的物理内存地址
0000 (16进制逻辑段地址左移四位,也就是16进制左移1位)
+ 0001 (16进制逻辑段内偏移地址)
-----------
00001 (16进制物理地址,也就是20位的二进制物理地址)
说明
下列所有关于CPU的描述中使用的段基址以及段内偏移地址都是以逻辑地址的形式进行说明的
因为寄存器中存放的全部都是逻辑地址
逻辑地址与物理地址的转变由CPU中的BIU部分(总线接口部件)中的地址加法器完成
关于CPU的执行
计算机去执行一条指令的时候并不是直接拿来执行的
而是要先去根据这个指令的二进制所存储的地址去取得这个指令的二进制,然后解译,执行
CPU的内部组成
运算器
对数据进行算数和逻辑运算。这些功能由算数逻辑单元(ALU arithmetic and logic unit)来实现
寄存器组
8086 CPU 中寄存器总共为 14 个,且均为 16 位 。
即 AX,BX,CX,DX,SP,BP,SI,DI,IP,FLAG,CS,DS,SS,ES 共 14 个。
cpu中的寄存器的数量对cpu运行速度的影响很大
寄存器组可以存放的类型如下:
- 数据(数据寄存器)
- 地址 (地址寄存器)
- 控制信息 (控制寄存器)
- 状态信息 (状态标志寄存器)
控制器
控制器是指挥与控制计算机各功能协同工作,自动执行计算机程序的部件。
CPU的编程结构
编程结构呢,就是从程序员和使用者的角度看到的结构,就像思维导图一样,是为了便于我们分析和理解,区别于真正的物理结构。
那么8086CPU的编程结构如下:
编程结构下的CPU
在编程结构下,按功能可将8086CPU分为两个部分:
- 总线结构部件BIU(Bus Interface Unit)
- 执行部件EU(Execution Unit)
总线接口部件BIU的组成
总线接口单元BIU主要负责与外界联系,也就是使CPU具有与片外存储器或者I/O接口电路进行数据交换的能力。
准确来说它有如下功能:
- 从内存中取指令送到指令队列
- 在CPU执行指令时,配合EU从指定的内存单元或I/O端口读取数据,再将数据传送给EU,由EU去执行。
- 把EU执行的结果传送到指定的内存单元或I/O端口
它由如下几部分组成:
4个段地址寄存器(16位)
4个段地址寄存器中分别存放了其对应功能内存段的基址
CS(Code Segment)
段寄存器CS指向存放程序的内存段,IP是用来存放下条待执行的指令在该段的偏移地址,把它们合在一起可在该内存段内取到下次要执行的指令。
DS(Data Segment)和 ES(Extra Segment)
段寄存器DS指向数据段,ES指向附加段,在存取操作数时,二者之一和一个偏移量合并就可得到存储单元的物理地址。该偏移量可以是具体数值、符号地址和指针寄存器的值等之一,具体情况将由指令的寻址方式来决定。
SS(Stack Segment)
段寄存器SS指向用于堆栈的内存段,SP是用来指向该堆栈的栈顶,把它们合在一起可访问栈顶单元。另外,当偏移量用到了指针寄存器BP,则其缺省的段寄存器也是SS,并且用BP可访问整个堆栈,不仅仅是只访问栈顶。
指针指令寄存器IP
这个寄存器的功能是指向下一条要执行的指令
准确来说应该是IP寄存器存放下条待执行的指令在代码段中的段内偏移地址
这里需要明确的是IP寄存器并不是存放当前正在执行的指令
而在代码段中的段内偏移地址哦~
20位地址加法器
地址加法器的作用就是将 四个段地址其中一个的基地址 和 一个段内偏移地址 进行一定的运算,最终得出一个存储单元的物理地址
那么我们怎么知道当前是哪个基地址和段内偏移地址呢?
这就需要看当前CPU进行的是什么操作了
如果CPU中的BIU是要取指令的话,那么基地址就是CS段寄存器中存放的基地址,段内偏移地址就是指令指针寄存器IP中存放的偏移地址
如果CPU中BIU当前是要读写数据的话,那么基地址就是DS或者ES中存放的基地址,段内偏移地址址就是某个寄存器中存放的偏移地址呢
6个字节(一个字节是8bit)的指令缓冲队列
8086的指令缓冲队列有6个字节, 当指令缓冲队列出现2个空字节, BIU就自动执行一次取指令周期,将下一条要执行的指令从内存单元读入指令缓冲队列。它们采用“先进先出”原则(可以理解成一个管道,先进去的就从下面漏下来了),按顺序存放,并按顺序取到EU中去执行。
当EU执行一条需要到存储器或I/O端口读取操作数的指令时,BIU将在执行完现行取指令的存储器周期后的下一个存储周期,对指令所指定的存储单元或I/O端口进行访问,读取的操作数经BIU送EU进行处理。
当EU执行跳转、子程序调用或返回指令时(比如c语言中的goto关键字编译成汇编语言之后就是执行跳转到某一条指令),BIU就使指令缓冲队列复位,并从指令给出的新地址开始取指令,新取的第1条指令直接经指令队列送EU执行,随后取来的指令将填入指令缓冲队列。
总线控制电路
是微处理器与外界总线连接的电路
在8086中包括三组总线:20位地址总线,16位双向数据总线,一组控制总线
执行部件EU的组成
执行部件EU负责指令的执行
它包含了算数逻辑单元(ALU)以及EU控制系统
ALU是16位加法器,用于对寄存器和指令操作数进行算术或者逻辑运算。
EU控制系统接受从BIU的指令缓冲队列中取出的指令代码,然后对其进行译码,和向EU内有关部分发出时序命令信号。
它由如下几部分组成:
4个通用寄存器AX,BX,CX,DX
这四个通用寄存器可以做为4个16位寄存器使用,也可以作为8个8为寄存器使用
当做为8位寄存器使用的时候
每个16位寄存器可以分为两个8位寄存器
如:16位寄存器AX可以分为AH(高8位)和AL(低8位)
当4个通用寄存器中某个当做16位寄存器使用的时候进行的是字操作,一次操作两个存储单元的数据(1个存储单元是8位数据,也就是一字节,2字节为一个字)
当做8位寄存器使用的时候进行的是字节操作
AX也成为累加器,cpu的许多指令都是利用累加器来执行的,一般都在运算指令执行前,累加器中存放一操作数,指令执行后,由累加器保存运算结果。
4个专用寄存器SP、BP、SI、DI
SP(Stack Pointer)为堆栈指针寄存器,当进行栈操作的时候,SP中存放的始终是栈顶地址
BP(Base Pointer)为基址指针寄存器,它的用途有点特殊,是和堆栈指针SP联合使用的,作为SP校准使用的,只有在寻找堆栈里的数据和使用个别的寻址方式时候才能用到。
example:
比如说,堆栈中压入了很多数据或者地址,你肯定想通过SP来访问这些数据或者地址,但SP是要指向栈顶的,是不能随便乱改的,这时候你就需要使用BP,把SP的值传递给BP,通过BP来寻找堆栈里数据或者地址.一般除了保存数据外,可以作为指针寄存器用于存储器寻址,此时它默认搭配的段寄存器是SS-堆栈段寄存器.BP是16位的,再扩充16位就是EBP,用于32位编程环境的.一般高级语言的参数传递等等,转换为汇编后经常由BP/EBP来负责寻址\处理.
SP,BP一般与段寄存器SS 联用,以确定堆栈寄存器中某一单元的地址,SP用以指示栈顶的偏移地址,而BP可 作为堆栈区中的一个基地址,用以确定在堆栈中的操作数地址。
在一般编写程序的时候我们习惯使用SP指向栈顶,BP指向栈底。
SI(Source Index 源变址寄存器)和 DI( Destination Index 目标变址寄存器)
SI、DI寄存器一般与数据段寄存器DS配合使用,由于SI寄存器有自动增量功能,DI寄存器有自动减量功能,所以用于变址还是很方便的。
在串处理指令(是一种汇编指令)中SI 和DI作为隐含的源变址和目的变址寄存器,此时 SI和DS联用,DI和ES联用,分别达到在数据段和附加段中寻址的目的。
标志寄存器Flag(16位)
该寄存器中7位是未用的,0-7位与Intel的8位微型机相同
其余的9位每一位都是一个标志
分为如下两部分
- 状态标志:SF、ZF、PF、CF、AF、OF
状态标志表示了前面的操作执行后,ALU处于何种状态,可能会影响到后面的操作- 控制标志:DF、IF、TF
人为设置的、可以用专门的设置、删除指令。对某种功能起限制作用
状态标志:
- SF(符号标志Sign Flag)和运算结果的最高位相同, 指出了当前运算后的结果是正是负。
- ZF(零标志Zero Flag) 若当前运算结果为0,ZF为1,否则ZF为0。
- PF (奇数/偶数标志 Parity Flag)如果运算结果中的低八位中所含的1的个数为偶数、PF为1,否则为0。
- CF(进位标志Carry Flag)如果加法操作使最高位产生进位,或减法操作使最高位有借位的时候,CF为1,循环操作也会影响这一标志。
- AF(辅助进位标志Auxiliary Carry Flag)加法运算时,如果第三位向第四位进位,或在减法运算时,第四位向第三位借位时,AF为1,否则为0。(常用于BCD码调整)
- OF(溢出标志Overflow Flag)在运算过程中如果操作结果超过了模值能表示的数值范围的时候称之为溢出,OF为1,否则为0。
控制标志:
- DF(方向标志Direction Flag)操作串操作命令方向的标志。
如果DF为0,串操作过程中地址自增,如果为1,串操作过程中地址自减。(上文说SI、DI两个寄存器拥有自增自减的功能,就是用这个标志位去影响的)- IF(中断标志Interrupt Flag)控制可屏蔽中断的标志。
如果IF为0,CPU不能对可屏蔽中断进行响应,为1时则可对可屏蔽中断做出响应。- TF(跟踪标志Trap Flag)
当追踪标志TF被置为1时,CPU进入单步执行方式,即每执行一条指令,产生一个单步中断请求。这种方式主要用于程序的调试。
时钟周期
CPU的晶振的工作频率的倒数。是计算机中最基本的时间单位。
机器周期
完成一个基本操作的时间单元,如取指周期,取数周期。
指令周期
执行一条指令所需的全部时间。一般以机器周期为单位,分单指令执行周期、双指令执行周期等。现在的处理器的大部分指令 (ARM、DSP)均采用单指令执行周期。
总线周期
CPU对存储或外设读写一次所需的时间,最基本的总线周期包含四个时钟周期。
在一个最基本的总线周期中,习惯上将四个时钟周期称为四个状态,分别是T1,T2,T3,T4
在T1状态时,CPU往地址/数据总线上发出地址信号, 指出要寻找的存储单元或外设端口的地址
在T2状态时,CPU从总线上撤销地址,从而使16位地址/数据总线 浮置为高阻状态,为数据传输做准备。
在T3状态时, 16位地址/数据总线上出现CPU要读写的数据,如果存储器或者I/O接口速度慢,CPU会插入一个或者多个附加的TW状态,也称为等待状态
在T4状态时,完成对数据的读写操作,总线周期结束。
ps:如果在当前CPU不从存储器或者I/O接口读写数据,那么总线处于TI状态,也称为空闲状态。
最大模式和最小模式
8086/8088可以在两种模式下工作:最大模式和最小模式,取决于硬件。
最小模式下整个微型计算机系统只有一个CPU,所有的总线控制信号都由这个CPU直接产生,因此,总线的控制电路被减到最小。
最大模式下整个微型计算机系统包括两个或两个以上的CPU,其中一个作为主处理器,其他的称为协处理器,协助主处理器进行工作。