计算机系统原理

数据存储

1b(bit)是一个二进制位,值为0/1
1B(byte)是一个字节,=8b
一般都至少以B为单位

单位n(210*n字节/单位)
KB1
MB2
GB3
TB4
不常用
PB5
EB6
ZB7
YB8

进制(2/8/10/16)

用于数字的计算和表示,n进制表示满n进1

进制用表示的字符集识别前缀
2[0-1]
8[0-7]0
10[0-9]
16[0-9],[a-f] (a-f分别表示10-15,字母大小写均可)0x,大小写均可

比如 0x12
表示16进制数12
10进制值为18

原码/反码/补码

以32位数举例
符号位:0位正,1位负
原码:第一位符号位,后面31位表示绝对值大小
反码:在原码基础上,如果是负数,后面31位取反
补码:在反码基础上,如果是负数,+1
一组相反数,两数相加会最终导致溢出(原来的32位全为0,最前面溢出一个1),最终结果(取32位)是0
一般的数全都用补码表示(在正数范围内。原码与反码一样),这样就很方便地将减法变成加法

一个正数变负数(x->-x),以32为为例,有3种方法,第一种 最方便理解;第二种 最快&最简单使用;第三种 最原始但慢

  1. 负数(-x)的机器数等于2^32-正数(x)
  2. x的机器数从右向左,找到第一个1,这个1左边的未全部取反
  3. 按照 原码->反码->补码 的顺序算

数的表示

有符号数均用补码表示

字符/指针

字符 16b有符号数
指针 无符号数(位数取决于机器是多少位)

整数

整数的大小用补码表示

类型含义
short(有符号)短整数,16b
unsigned int无符号短整数,16b
int整数,32b
unsigned int无符号整数,32b
long long长整数,64b
unsigned long long无符号长整数,64b

浮点数(float/double)

使用IEEE754标准,
float32b,double64b

类型符号/阶码/位数
float1/8/21
double1/11/52

数= +/- 1.尾数*2^阶码
尾数为2进制无符号整数(可以通过10进制的0.xxx *2取整获得),最后有多余则进1
阶码为有符号整数

整数的输入与转换

C90/C99

数值范围C90C99
0~2 ^ 31-1intint
2^31 ~ 2^32-1unsigned intlong long
2^32 ~ 2^63-1long longlong long
2^63 ~ 2^64-1unsigned long longunsigned long long

解析

先将数拿进来,不看前面的符号(即看它的绝对值)给一个类型,再看符号对机器数进行一定规则的转换(主要是 正变负)
按照不同的C语言标准

整数类型转换

位截断/位扩展(位数不同)

位截断:比如64位数转成32位数,就是很粗暴地把前面不需要的扔掉

位扩展:把32位数转成64位数,分为 0扩展 和 符号扩展
符号数采用符号扩展:前面多出来的位补符号位
符号数采用0扩展:前面多出来的位补0

位数相同,有无符号不同

直接将机器码copy过去

计算时的统一类型

尾数 短->长
符号 有->无
精度 小->大,(能表示的最小的整数 越小,精度越大)

比如 2147483647>-2147483648 (C90)的值为false
因为 2147483648被理解为unsigned int,机器数为(10…),
再看前面的负号,机器数改变后还和之前一样
而比较时,类型会统一为unsigned int,左边的那个数机器码不变,(
按无符号数比较)左边(01…)明显<右边(10…)

float与int都是32位,
float转int时会丢失精度(小数位丢失)
int能表示的最大的数比float大

浮点数运算

加减

对阶(小阶对大阶)

提出那个阶数比较大的那个
目的:使两个(每个)浮点数的较大的整数部分 为1

左归/右归

左归:数相对小数点向左移,次数-1
右归:数相对小数点向右移,次数+1
目的:保持整数位为1,方面再次表示为浮点数

乘除(较复杂,忽略)

指令集架构(ISA)

生成机器代码步骤

使用gcc,可以直接一步完成(gcc 文件名 -o 目标文件名)
也可以分步来(当不写命令选项时,直接将后续的步骤都做了)
命令格式:gcc -命令选项 文件名 -o 目标文件名
一开始的文件(.c)
第几步 -命令选项 目标文件格式 详细

  1. (-E -i)预处理:处理#开头的语句(#include,#define)
  2. (-S -s)编译:生成汇编语言程序
  3. (-c -o) 汇编:生成可重定位文件(二进制)
  4. (无命令选项 文件无后缀)连接(包括符号解析和重定位):生成可执行文件(二进制)

寄存器

寄存器名称大小/内部结构
通用寄存器32b
EAX累加器AX:低16b(AH:高8b;AL:低8b)
EBX基址寄存器BX:低16b(BH:高8b;BL:低8b)
ECX计数寄存器CX:低16b(CH:高8b;CL:低8b)
EDX数据寄存器DX:低16b(DH:高8b;DL:低8b)
ESP(栈顶)栈指针寄存器SP:低16b
EBP(栈底)基址指针寄存器BP:低16b
ESI源变址寄存器SI:低16b
EDI目标变址寄存器DI:低16b
专用寄存器32b
EIP指令指针寄存器IP:低16b
EFLAGS标志寄存器FLAGS:低16b
段寄存器16b
CS代码段
SS堆栈段
DS数据段
ES附加段
FS附加段
GS附加段

寻址方式(地址表示)

寻址方式机器指令格式书面格式
寄存器寻址%寄存器名R[寄存器名],如eax
存储器寻址偏移量(基址,变址,比例系数)M[地址]
立即数寻址$值

存储器寻址的地址计算:偏移量+基址+变址*比例系数
存储器寻址每一个值都可以省略(比例系数的默认值为1,其他的为0),但()内的如果后面的数省略了,逗号也可以相应地省略(如果把前面的省略了而后面的数不省略,需要把逗号补上)

标志位(EFLAGS)

常用的有

标志位含义判断标准
常用
OF(overflow flag)溢出标志左1进位 && 左2进位
SF(sign flag)符号标志=符号位
ZF(zero flag)零标志运算结果是否为0
CF(carry flag)进/借 位标志是否移除或(因不够减而)借位
控制标志(次常用)
DF(firection flag)方向标志
IF(interrupt flag)中断允许标志
TF(trap flag)陷阱标志

OF用于判断有符号数是否溢出
CF用于判断无符号数是否溢出

指令

数据传送地址数功能
mov2地址2的数=地址1的数
movs2送数据同时符号扩展
movz2送数据同时0扩展
xchg2交换两个数
push1将地址里的数入栈
pop1出栈一个数到地址1里
地址传送指令
lea地址2的数=地址1
输入输出
in1
标志传送
pushf1
popf1
函数调用与返回
call1函数的目标地址
leave0
ret0函数返回
算数运算
add2地址2的数+=地址1的数
sub2地址2的数-=地址1的数
cmp2地址2的数-地址1的数,数不改变大小,常用于跳转的计算
inc1地址1的数++
dec1地址1的数–
neg1地址1的数=-地址1的数
mul2有符号数相乘,
高位-低位(AX(16b16b)/DX-AX(32)/ECX-EAX(64))=地址2的数地址1的数
imul2无符号数相乘
div2有符号数相除,
商-余数(AX(16)/DX-AX(32)/ECX-EAX(64))=地址2的数/地址1的数
idiv2无符号数相除
逻辑运算
not1按位取反
and2按位与
or2按位或
xor2按位异或
test2地址2的数&地址1的数,数不改变大小,常用于跳转的计算
位移
shl2逻辑左移
shr2逻辑右移
sal2算数左移
sar2算术右移
rol2循环左移
ror2循环右移
rcl2带循环左移
rcr2带循环右移
跳转均为1,跳转的目标地址有条件跳转 是 根据(因test/cmp产生的)符号位的情况决定是否跳转
条件说明
jmp无条件无条件跳转到对应地址
jcCF==1有 进/借 位
jncCF==0无 进/借 位
je/jzZF==1==0
jne/jnzZF==0!=0
jsSF==1是负数
jnsSF==0是非负数
joOF==1有溢出(有符号数)
jnoOF==0无溢出(有符号数)
下面是无符号数跳转
ja/jnbeCF== 0 && ZF==0>0
jae/jnbCF== 0 || ZF==0>=0
jb/jnaeCF== 1 && ZF==0<0
jbe/jnaCF== 1 || ZF==0<=0
下面是有符号数跳转
jg/jnleSF== OF && ZF==0>0
jge/jnlSF== OF || ZF==1>=0
jl/jngeSF!= OF && ZF==0<0
jle/jngSF!= OF || ZF==1<=0

栈 / 函数的调用过程

栈在程序运行时分配,从0x08048000开始,向下延申
即 栈底(EBP)在上,栈顶(ESP)在下
(EBP保存栈底,ESP保存栈底)

函数P(caller,调用者)调用函数Q(callee,被调用者)
过程调用的执行步骤如下

  1. P将入口函数(实参)放到Q能访问到的地方
  2. P将返回地址存到特定的地方,然后将控制转移到Q
  3. Q保存P的现场,并为自己的非静态局部变量分配空间
  4. 执行Q的函数体
  5. Q回复P的现场,并释放局部变量所占空间
  6. Q取出返回地址,将控制转移到P

形象地说,就是

调用者在调用函数前,保存自己的返回地址(即下一条指令的地址,保证返回时可以继续执行程序)和EAX、ECX、EDX寄存器
在调用函数后,控制权转到被调用者
被调用者将 调用者的栈底、需要用到的被调用者保存寄存器 push进栈,然后可以开辟自己的栈
运行完被调用者函数体
将 调用者的栈底、需要用到的被调用者保存寄存器 pop出栈
控制权转到调用者
pop出EAX、ECX、EDX寄存器和返回地址
继续运行调用者的函数体

调用者保存寄存器:EAX,ECD,EDX
被调用者保存寄存器:EBX,EDI,ESI
开辟的栈的大小要是16的倍数(返回地址和上一个EBP除外)

程序的链接

连接前后的文件结构

链接视图(链接前)执行视图(链接后)
ELF头ELF头
程序头表(段头表,可选)程序头表(段头表,必须)
节1段1
节头表(必须)节头表(可选)

可选的一般不需要

ELF头

包含文件结构说明的信息

节/段 头表

由若干表项组成,每一项有(节/段 名、偏移、大小、访问属性、对齐方式)

链接视图(节)

文件结构
ELF头
程序头表(段头表,可选)
.text目标代码
.rodata只读数据,如printf的格式串,switch的跳转表
.data已初始化的全局变量
.bss未初始化的全局变量
.stmtab符号表
.rel.text.text节的重定位信息
.rel.data.data节的重定位信息
.debug调试用符号表
.lineC行号与.text节机器指令的映射
.strtab字符串表,包括.symtab和.debug节中的符号以及节头表中的节名
节头表(必须)

执行视图(段)

段与节差别不大(知识在执行视图里,所以叫段)
与节相比,少了.rel.data和.rel.text,多了.init和.fini

  • .init:在程序运行后,进入主函数之前,运行里面的指令代码
  • .fini:主函数运行完后,程序结束前,运行里面的指令代码

下面的每一块根据运行时的 是否载入与**(载入后)读写权限**的不同,分为3类

  • 只读(代码)段
  • 可读写(数据)段
  • 不需映射到存储空间的符号表和调试信息
文件结构
只读(代码)段
ELF头
程序头表(段头表,必须)
.text目标代码
.init、.fini在主函数运行前后运行
.rodata只读数据,如printf的格式串,switch的跳转表
可读写(数据)段
.data已初始化的全局变量
.bss未初始化的全局变量
不需映射到存储空间的符号表和调试信息
.stmtab符号表
.debug调试用符号表
.lineC行号与.text节机器指令的映射
.strtab字符串表,包括.symtab和.debug节中的符号以及节头表中的节名
节头表(可选)

符号解析

符号:全局变量名和全局函数名

全局符号:非静态 函数名/全局变量名
外部符号:extern定义的符号
本地符号:静态 函数名/全局变量名

强符号:全局符号
弱符号:外部符号,本地符号

符号解析:将每个符号的引用与符号的定义建立连接
每个符号最多1个强定义(多个强定义会直接报错 ;某个符号没有强定义会指定其中的一个并警告,不会报错)

重定位

把指令里的符号与它的定义绑定起来(给一个确定的地址)

相/绝 对定位

绝对定位:R_386_32,用于函数的定位
相对定位:R_386_PC32,用于变量的定位

动态链接

使用网络上的动态链接库

程序的执行

1条命令完成需要5个步骤

  1. 取指令(IF)
  2. 指令译码(ID)
  3. 取操作数(OF)
  4. 执行(EX)
  5. 写回(WB)

这5个步骤分别用到了5个部件
一般指令执行采用流水线的方式,让各个部件适时都在运转(提高效率)
但流水线每一步完成之后都需要将结果 送寄存器,但总体来说,效率大大提高
单位一般为ps(10-12s)
记6个时间分别为t1-t5,t0

时钟周期:平均每条指令运行时间
流水线前:t1+t2+t3+t4+t5
流水线后:max(t1,t2,t3,t4,t5)+t0
指令吞吐率:1/时钟周期。单位一般为GIPS(每秒多少*109条指令,109instruction per seconds)

缓存(cache)

在CPU内部,用于加快内存访问速度
有时会有多级cache

  • L1 cahce:1级cache,更关注速度而不要求很高的命中率。通常采用分离cache,分为d-cache(data,数据)和i-cache(instruction,指令)
  • L2 cahce:2级cache,更关注命中率,不分离
  • 有时也有 L3 cahce:3级cache,多个核心共用

映射

缓存分为n行(n一般=2i,每行有个号,顺序从0开始),每行有(有效位(1b) 标记 数据(有个大小))组成
按照cache行的数据大小,对主存分块(形成了块号,以地址的部分作为块号)

这样将很抽象,举个例子(简单但不实际)
缓存16(24)行,每行的数据为4KB(212)
主存256KB(218),每字节的顺序(地址)用16b表示
/
主存被分为64(256K/4K=64=26)块,每块4KB(212),块的顺序用6b表示(块地址的前6位)

直接映射

按照cache的行数,对块分(形成了群号),每个块在自己的群中有自己的位置
这时,cache的行 与 单个群里的块的顺序一一对应
所以只需要相应的群号作为标记即可

主存有64(26)块,缓存有16(24)行,
主存块就被分成4(22)群,群的顺序用2b表示(块地址的前2位)
标记的大小就是2b

全相联映射

不分群,每一块都可以映射到cache的任何一行
所以只需要相应的块号作为标记即可

主存块有64(26)块,不分群
标记的大小就是6b

组相联映射

按一定数量对cache的行进行分(形成组数)
按照cache的组数,对块分(形成了群号),每个块在自己的组中有自己的位置
这时,cache的组 与 单个群里的块的顺序一一对应
单个群里的块可以放到对应组的任何一行
所以只需要相应的群号作为标记即可

可以把直接映射(每个cache组 1行)和全相联映射(把整个cache看成1组)看成特殊的组相联映射

假设cache 2(21)行一组,cache被分为8(16/2=8=23)组
主存块有64块,cache有8组
主存块被分为8(64/8=8=23)群,群的顺序用3b表示(块地址的前3位)
标记的大小就是3b

替换算法(策略)

cache已满但要放入新内容时 用到的算法

先进先出(FIFO,First-In-First-Out)

对每一行计数,每次+1,把最大的换掉,并将该数置0。
命中时不置0

最近最少用(LRU,Least-Recently Used)

对每一行计数,每次+1,把最大的换掉,并将该数置0
命中时置0(与先进先出的差别)

最不经常用(LFU,Least-Frequrntly Used)

对每一行的使用次数计数,把最少的换掉

随机替换

随便找一行出来换换掉

时/空 间局部性

时间性:访问地越频繁,时间性越好
空间性:访问的地址越连续,空间性越好(比如数值,对于单个变量没有空间性)

上面两个的意义是,
世界性:频繁访问,这样它会一直在cache中,而不必每次都从内存访问
空间性:从内存中取内存的时候,通常会把它后面一些的块也取进来,访问到后面的数时(比如数组),数在cache中

~虚拟存储

分页式

页表

地址转换

快表

CPU访存过程

分段式

段页式

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值