跳至主要內容

ComputerSystems

LincZero大约 12 分钟

ComputerSystems

目录

寻址分析

汇编代码能直接操作地址,当然只能操作CPU寻址范围内的地址 像GPU中的非共享地址则无法直接操作,但获取可以依赖编写共享内存的部分的汇编代码来间接编辑或访问

寻址范围(8086 CPU)

几种存储单元

8086处理器 (x86鼻祖):16位处理器

寻址空间:一共20跟寻址线,2^20=1MB的空间 (按道理16位寄存器的寻址范围是2^16=64KB,这里居然超了,被拓展多了4根线。当然段寄存器和IP寄存器都依旧是16位的)

存储单元之间的数据传输关系(mov)

4种数
4种数

内部 寻址范围

寄存器组

8086cpu内部寄存器结构图 (注意如果是32/64位寄存器,有的会多一个前缀,例如ip->rip,sp->rsp)

  • 通用寄存器 (General Register)
    • 数据寄存器 (Data Register)
      • AH|AL、AX累加器
      • BH|BL、BX基址寄存器
      • CH|CL、CX计数寄存器
      • DH|DL、DX数据寄存器
    • 变址/索引寄存器 (Index Register)
      • SI、源变址寄存器 (Source Index)
      • DI、目的变址寄存器 (Destination Index)
    • 指针寄存器 (Pointer Register)
      • BP、基址指针 (Base Pointer)
      • SP、堆栈指针 (Stack Pointer)
  • 控制寄存器 (Control Register)
    • IP、指令指针 (Index Pointer,而不是Internet Protocol)
    • FLAGS、标志寄存器 (Flags)
  • 段寄存器 (Segment Register)
    • CS代码段寄存器 (Code Segment)
    • DS数据段寄存器 (Data Segment)
    • ES扩展段寄存器 (Extended Segment)
    • SS堆栈段寄存器 (Stack Segment)
80x86可见寄存器组
image-20211110020325687
image-20211110020325687

通用寄存器 (8086)

8086 16位寄存器:有8个16位通用寄存器,其中4个可以拆成2个8位寄存器

image-20211109192301811
image-20211109192301811

通用寄存器 (64位)

64位通用寄存器:可以看到有很多的历史残留问题,导致命名不统一

IMG_20200924_063958
8个字节前4字节前2字节前1字节含义地址特征
%rax%eax%ax%al返回值r?x,e?x,?x,?l【rule1】
%rbx%ebx%bx%bl被调用者保存r?x,e?x,?x,?l【rule1】
%rcx%ecx%cx%cl第4个参数r?x,e?x,?x,?l【rule1】
%rdx%edx%dx%dl第3个参数r?x,e?x,?x,?l【rule1】
%rsi%esi%si%sil第2个参数r?i,e?i,?i,?l【rule2】
%rdi%edi%di%dil第1个参数r?i,e?i,?i,?l【rule2】
%rbp%ebp%bp%bpl被调用者保存r?p,e?p,?p,?l【rule3】
%rsp%esp%sp%spl栈指针(stack pointer)r?p,e?p,?p,?l【rule3】
————————————(前后8行分割线)
%r8%r8d%r8w%r8b第5个参数r?p,r?d,r?w,r?b【rule4】
%r9%r9d%r9w%r9b第6个参数r?p,r?d,r?w,r?b【rule4】
%r10%r10d%r10w%r10b调用者保存r?p,r?d,r?w,r?b【rule4】
%r11%r11d%r11w%r11b调用者保存r?p,r?d,r?w,r?b【rule4】
%r12%r12d%r12w%r12b被调用者保存r?p,r?d,r?w,r?b【rule4】
%r13%r13d%r13w%r13b被调用者保存r?p,r?d,r?w,r?b【rule4】
%r14%r14d%r14w%r14b被调用者保存r?p,r?d,r?w,r?b【rule4】
%r15%r15d%r15w%r15b被调用者保存r?p,r?d,r?w,r?b【rule4】

图表记法:(该死的,这命名好混乱,记不注,用时得查)

有含义的部分:(除了特别的几个,其他的就不记了,需要用再查,命名很混乱)

  • 8位的前8个寄存器:根据英文缩写来记
  • 16位部分:abcd,x和l。例如:高8位ah-high,低8位是al-low,结合起来是ax
  • 32位部分:前缀e:extended扩展
  • 64位部分:前缀r:row行,r8~r15。_dwb分别表示_/double/word/byte,分别是四字/双字/字/字节

新命名均r开头,后8个寄存器均r+数字开头

前四个寄存器均x/l结尾,前一个位为a/b/c/d

所有16个寄存器的低位部分都可以作为自己、字(16位)、双字(32位)、四字(64位)数字来访问

那么当复制和生成1、2、4字节时,对于高位的处理有两条规则(生成8字节时:没有剩余字节)

  • 生成1、2字节时:保留前面剩下的
  • 生成4字节时:高位的4字节置为0(该规则是作为从IA32x86-64扩展的部分而使用的)

其他寄存器

标志寄存器(属于控制寄存器)

调试汇编时输入r可以看到末尾的标志寄存器状态。大小写分别表示1和0

eflags 0x00000082: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf af pf cf
标志单词含义
CFCarry Flag进位
PFEven Flag偶数
AF
ZFZero Flag零标志
SFSign Flag符号位,负数位
TF
IF
DF
OFOverflow Flag溢出位

要结合Jcc(条件转移指令)来使用

jmp是直接跳转,而jcc是符合条件才会跳转

外部 寻址范围

寻址范围 - 全部

并不是显卡、BIOS等全部都映射到内存空间,只是将CPU寻址范围内的部分地址分配给其他设备

寻址范围(共1MB)分配给
0x00000~0x9FFFF64*11 = 640KB内存 DRAM
0xAFFFF~0xBFFFF64*02 = 128KB显卡(包括文字模式、图像模式在内的显示部分)
0xCFFFF~0xFFFFF64*04 = 256KB假装内存的BIOS(ROM芯片、显卡、硬盘等的BIOS)

寻址范围 - 内存 (IBM PC 5150)

IBM PC 5150中的ROM BIOS构成(历史残留)

内存范围(共640KB)分配给
0x0000~0x???? (最开头)未知BIOS产生的中断向量表以及BIOS的数据
(中间)未知其他
0x7c00~0x7FFF (最末端)1KB1024B大小的BootLoader

开机时DRAM内存空间的变化

  • (1) CPU收到RST信号,IP被强制定位到ROM,从ROM取指令和执行

  • (2) BIOS程序执行过程中,产生一个中断向量表的东西,DRAM被消耗了一部分

  • (3) BIOS执行完POST后,开始在外部存储设备中找BootLoader,并加载到内存里 而因为BootLoader将系统拉起来后内存就可以被释放了,所以它加载的位置在末端

    • 其中IBM PC 5150中支持的最小内存是32KB (0x0000~0x7FFF,8086则是64KB)

    • 其中BootLoader的Boot扇区也就是MBR扇区是512B栈/数据是512B,共计1KB

    • 也就是BootLoader的位置在:32KB-1KB+1=0x7c00

寻址范围 - 内存 (8086)

从IBM PC 5150的32KB,后来内存扩展到1MB到现在的数GB,这个0x07c00的位置也没变

内存范围(共640KB)分配给
0x00000~0x07BFF (最开头)未知BIOS产生的中断向量表以及BIOS的数据
0x07C00~0x07DFF (非最末端)512B1024B大小的BootLoader
0x07E00~0x07FFF (非最末端)512B1024B大小的BootLoader
0x08000~0x9FFFF (最末端)未知其他
  • 查看内存中cpu寻址的范围
    • 和下面显卡的查看方式不同,内存在设备管理器中居然是不显示的!
    • 内存可能很大 (4/8/16/32GB),但系统只给它分配几百MB的地址空间

寻址范围 - 显卡

这部分其实本质上就是内存,当时的显卡是集成显卡,没有独显。显然这部分的本质就是内存,可以当内存使用

显存范围(共128KB)分配给
0xA0000~0xAFFFF64KBEGA/VGA/XGA/XVGA 彩色图形
0xB0000~0xB7FFF32KBMono text video buffer 黑白文本
0xB8000~0xBFFFF32KBCGA/EGA + chroma text video buffer 彩色文本
  • 查看显存中cpu寻址的范围

    • Win+X > 设备管理器 > 显示适配器 > 选择你的显卡并双击 > 资源菜单 > 资源设置中可以看到分配给内存的显存范围
    • 显卡显存可能很大 (4/8/16/24GB),但系统只给它分配几百MB的地址空间 我的电脑:显存大小24GB,专用8GB,共享16GB。分配的显存大小??1664+16416^6*4+16^4 =64MB+64KB??(应该是我算漏了,少了)
  • 显存分配原理

    • 看起来比较小,但可以作为地址映射出去
    • 例如使用256MB来映射4GB的显存,显卡也存在自己的汇编指令(可以切换映射到地址空间的显存等。话说Shared是吗)

寻址方法

寄存器寻址方法(8086分段机制)

8086分段机制:解决16位寄存器无法访问全部1MB地址空间的问题,而且程序重定位也变得简单(ROM-BIOS或操作系统可以进行调度)

内存偏移

  • 代码段和数据段是分别的两段连续的内存(冯诺依曼架构)
  • CS寄存器:代码段的开头。CS指定代码段基准地址、IP指定代码段偏移地址
  • DS寄存器:数据段的开头。DS指定数据段基准地址、增加相应的偏移地址来访问数据
  • CPU的IP存储的不是绝对物理地址,而是相对于CS的偏移。CPU访问通过段地址: 偏移地址的模式来

段地址:偏移地址(8086中16位寄存器如何存储20位的寻址范围 )

寻址问题

  • 8086 CPU 20位,总寻址范围:2^20=1MB
  • 8086寄存器仅16位,能存储地址的数量:2^16=64KB
  • 即寄存器不能存储CPU寻址范围内的所有地址?——此时需要段地址:偏移地址的寻址方式

解决方法

  • 两个16位寄存器

    • 段寄存器:ds,存物理地址 16字节对齐:每个段的起始地址,都必须是16的倍数
    • 偏移地址寄存器:ax,存逻辑地址
  • 寻址范围分段

    • 8086的1MB空间切分时
    • 最少可分16个段,每段64KB(1MB = 16x64KB = (2^4)x64KB)
    • 最多可分64K个段,每段16B
    • 实际使用过程中灵活性很大,可以分为16、32、64、128......65536个段
  • 使用

    • 逻辑地址 = 段地址:偏移地址 = 段地址左移4位+便宜地址 = 实际物理地址

    • 扩展思考:一个物理地址可能由多少个逻辑地址来表示?

      • 举例:设物理地址0x00010 (17),此时两种

        段数逻辑地址
        160x0000::0x0010
        320x0000::0x0010
        ......0x0000::0x0010
        327680x0000::0x0010
        655360x0001::0x0000
      • 举例:设物理地址0xFFFFF,此时则比较麻烦,16-4+1=12种

代码段 - 汇编地址

PC是如何指导程序的位置呢?

汇编地址:

  • 编译时 汇编代码通过nasm编译成bin文件时,nasm会把汇编.asm当成一整个代码段, 里面的每一条指令都会有一个相对于代码头部的偏移地址,这个偏移地址就是汇编地址
  • 加载时 bin在虚拟机或真机上运行时,ROM-BIOS程序将Start.bin加载到内存。具体加载到内存的什么位置,视程序大小和内存闲置空间而定自动分配,分配好后该这段程序有了一个起始物理地址,该地址给CS (代码段寄存器)
  • 执行时 CPU按CS:IP (代码段寄存器:指令指针) 的逻辑地址去取指令

堆栈段 vs 数据段、代码段

栈段与代码段类似的

  • 代码段:CS (代码段寄存器) 保存代码段的基址,IP (指令指针) 保存相对于基址的偏移
  • 数据段:DS (数据段寄存器) 保存数据段的基址,ax等 (数据寄存器) 表示相对于基址的偏移
  • 栈段:SS (堆栈段寄存器) 保存代码段的基址,SP (堆栈指针) 保存相对于基址的偏移

区别

  • 内容不同
    • IP指向当前在运行的指令
    • SP也叫栈顶指针,指向栈顶
  • 方向不同
    • 栈的走向跟数据段或代码段不一样
    • 数据段和代码段:从内存低处向高处进行。例如代码从开始0x7c00,下条可能为0x7c02
    • 栈段:从内存高处向低处进行。push操作让sp减少,pop操作让sp增大

访问方式

原理

之前运行起来是在512字节的启动扇区写入,我们写的是BootLoader程序

  • CPU与统一编址设备之间使用mov存入寄存器并访问
  • CPU与独立编址设置之间使用in/out指令来访问,也是通过端口来访问的。端口本质是寄存器的代号

设备端口原理

  • 显卡、硬盘上都有自己的寄存器,8~16位不等
  • CPU读写不同的端口,实际上是在读写设备的寄存器
  • ICH (I/O Controler Hub)

使用汇编读取硬盘(寻址范围外)

计算机主硬盘分配了8个端口,0x1f0~1f7

从硬盘访问数据的方式

  • CHS (Cylinders Heads Sectors,柱面磁头扇区):需要磁头、柱面和扇区的信息。但这种方式太过繁琐,而且像固态硬盘根本没有这些信息的也不适合
  • LBA (Logical Block Addressing,逻辑块寻址):主流方式,比较快

虚拟内存

虚拟地址空间补充说明
内核虚拟内存【顶部区域】不允许应用程序读写和调用,必须通过调用内核来执行这些操作
用户栈
(往下增长)
运行时创建
【动态大小】每次调用函数栈增长,函数返回时栈会收缩
共享库的内存映射区域
(往上增长)
【动态大小】存放像C标准库和数学库这样共享库代码和数据的地方
运行时堆
(往上增长)
运行时由malloc创建
【动态大小】调用malloc和free这样的C标准库函数时可动态扩展和收缩
读/写数据从hello可执行文件加载进来的程序代码和数据
只读的代码和数据【开始区域】从hello可执行文件加载进来的程序代码和数据