虚拟地址物理地址等众多地址及MMU相关知识

先聊聊存储器

存储器是单片机结构的重要组成部分,存储器是用来存储编译好的程序代码和数据的,有了存储器单片机系统才具有记忆功能。按照存储介质的特性,可以分“易失性存储器”和“非易失性存储器”两类。易失性存储器断电后,里面存储的内容会丢失;非易失性存储器断电后,数据可以依然保持。
虚拟地址物理地址等众多地址及MMU相关知识

STM32单片机存储器

存储器组织:
程序存储器、数据存储器、寄存器和输入输出端口被组织在同一个4GB的线性地址空间内。 数据字节以小端格式存放在存储器中。一个字里的低地址字节被认为是该字的低有效字 节,而高地址字节是高有效字节。 外设寄存器的映像请参考相关章节。 可访问的存储器空间被分成8个主要块,每个块为512MB。 其他所有没有分配给片上存储器和外设的存储器空间都是保留的地址空间,请参考相应器件的 数据手册中的存储器映像图。(上面这段话来自STM32中文参考手册_V10)。
虚拟地址物理地址等众多地址及MMU相关知识
stm32的flash地址起始于0x0800 0000,结束地址是0x0800 0000加上芯片实际的flash大小,不同的芯片flash大小不同。RAM起始地址是0x2000 0000,结束地址是0x2000 0000加上芯片的RAM大小。不同的芯片RAM也不同。Flash中的内容一般用来存储代码和一些定义为const的数据,断电不丢失,
RAM可以理解为内存,用来存储代码运行时的数据,变量等等。掉电数据丢失。STM32将外设等都映射为地址的形式,对地址的操作就是对外设的操作。stm32的外设地址从0x4000 0000开始,可以看到在库文件中,是通过基于0x4000 0000地址的偏移量来操作寄存器以及外设的。

相关阅读:STM32学习笔记:读写内部Flash(介绍+附代码)

关于编译器生成的文件

下图是一个OLED时钟的工程,Keil编译后的编译信息
虚拟地址物理地址等众多地址及MMU相关知识
很多人并不一定知道Program Size的含义,我来给大家解释一下。
Code:是程序中代码所占字节大小;
RO-data:程序只读的变量,也就是带const的,和已初始化的字符串等;
RW-data:已初始化的可读写全局/静态变量;
ZI-data:未初始化的可读写全局/静态变量;

那么这个程序占用的Flash存储器的空间大小是多少呢?
程序所占Flash空间大小=Code+RO data+RW data=生成的bin文件大小。

那么这个程序占用的SRAM存储器的空间大小是多少呢?
程序固定占用RAM大小=RW data+ZI data。

这些信息除了在编译器下方的信息栏里面看到,也可以在项目文件里的.map文件的最下面找到,如下图:
虚拟地址物理地址等众多地址及MMU相关知识

数据在存储器上的存储结构

程序在Flash上的存储结构如下图所示,通过阅读hex文件和MDK下调试综合提炼出来的。其中,ZI-data对应未初始化数据段,RW-data对应已初始化数据段,Code对应代码段。
虚拟地址物理地址等众多地址及MMU相关知识
数据在SRAM上的结构,如下图所示。这部分大家可以参考上面的内存映射图来理解。
虚拟地址物理地址等众多地址及MMU相关知识

物理地址、虚拟地址、线性地址和逻辑地址

虚拟地址的提出,主要是为了解决在操作系统中,多线程内存地址重复,大进程在小内存运行等问题 , 在32位系统中,虚拟地址空间中有4G,在操作系统中程序中使用的都是虚拟地址。

在讲述这些东西之前,推荐阅读:20 张图揭开内存管理的迷雾,阅读过后,下面文章可以跳读提高阅读效率。

物理地址

这个是最容易理解的,计算机的主存以字节为单元进行组织,为方便CPU访问主存的任一单元而对物理内存单元进行编号,这个编号就是地址,即物理地址,即CPU通过地址总线可寻址的物理单元。物理地址是内存的真实地址。

这里说的物理地址是内存中的内存单元实际地址,不是外部总线连接的其他电子元件的地址!

再次说明,物理地址属于比较好理解的,物理地址就是内存中每个内存单元的编号,这个编号是顺序排好的,物理地址的大小决定了内存中有多少个内存单元,物理地址的大小由地址总线的位宽决定。

虚拟地址

给大家看一张图:
虚拟地址物理地址等众多地址及MMU相关知识
虚拟地址是CPU保护模式下的一个概念,保护模式是80286系列和之后的x86兼容CPU操作模式,在CPU引导完操作系统内核后,操作系统内核会进入一种CPU保护模式,也叫虚拟内存管理,在这之后的程序在运行时都处于虚拟内存当中,虚拟内存里的所有地址都是不直接的,所以你有时候可以看到一个虚拟地址对应不同的物理地址,比如A进程里的call函数入口虚拟地址是0x001,而B也是,但是它俩对应的物理地址却是不同的,操作系统采用这种内存管理方法。

1、是防止程序对物理地址写数据造成一些不可必要的问题,比如知道了A进程的物理地址,那么向这个地址写入数据就会造成A进程出现问题,在虚拟内存中运行程序永远不知道自己处于内存中那一段的物理地址上!

现在操作系统运行在保护模式下即便知道其他进程的物理地址也不允许向其写入!但是可以通过操作系统留下的后门函数获取该进程上的虚拟地址空间所有控制权限并写入指定数据,详细会在反汇编编程中教给大家!

2、虚拟内存管理采用一种拆东墙补西墙的形式,所以虚拟内存的内存会比物理内存要大许多。

在进入虚拟模式之前CPU以及Bootloader(BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境),操作系统内核均运行在实模式下,直接对物理地址进行操作!

虚拟内存中也有分页管理,这种管理方法是为了确保内存中不会出现内存碎片,当操作系统内核初始化完毕内存中的分页表后CPU的分页标志位会被设置,这个分页标志位是给MMU看的!

逻辑地址

CPU产生的虚拟地址,即CPU指令中的一条用来指定操作数或指令的一条地址。

现代操作系统一个很重要的概念即保护模式,通过层层的保护,避免程序的越权执行。例如访问只读代码段、访问不存在的地址等。逻辑地址由以下两部分构成,16bit的段选择符和32bit的偏移量。通过段描述符判定相关权限等机制,完成相应的转化,转化为一条线性地址,用于通过分页机制进行物理寻址。下图为一条逻辑地址的构成,以及由逻辑地址翻译为线性地址的描述
虚拟地址物理地址等众多地址及MMU相关知识

线性地址

线性地址(Linear Address)是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。

要了解线性地址,先熟悉地址空间的概念。

地址空间(address space),是一个非负整数地址的有序集合:{0,1,2,…}。如果地址空间中的整数是连续的,就说它是一个线性地址空间。一个地址空间的大小,由构成该地址空间的最大位数决定。

现代操作系统为了实现对物理内存的最大使用以及进程资源的彼此隔离等功能,发明了虚拟地址空间的概念。每一个进程维护各自的虚拟地址空间,对于32位的地址空间,其最大可用大小为2^32B,即4GB。而该空间内的地址连续,故称为线性地址空间,该空间内的每一条地址均为一个线性地址。

由于现代操作系统采用分页机制,利用多级页表的方式管理虚拟地址空间,所以线性地址多被划分为多个区段,每个区段内的数值作为某一级页表内的偏移量,用与索引下一级页表。于是有了大家熟知的线性地址的结构:
虚拟地址物理地址等众多地址及MMU相关知识

这些地址之间的关系

虚拟地址只是逻辑地址与线性地址的一个统称,经过翻译转化成为物理地址,达到访存目的。
虚拟地址物理地址等众多地址及MMU相关知识
逻辑地址(虚拟地址)— 分段 --> 线性地址(虚拟地址) — 分页 —> 物理地址

关于分段分页和内存碎片等请阅读:20 张图揭开内存管理的迷雾

总结

有网友表示:单片机里没有‘虚拟地址’这个概念,而是‘物理地址’,而在ARM领域,Linux编程的时候,就会涉及到‘虚拟地址’,经过MMU以后才是物理地址。
经过与一些网友的交流,我的想法:说单片机没有虚拟地址应该不太正确,但是这点楼主也很纠结对错。我上面来自STM32中文参考手册_V10的那段话表明,我们通常见到的STM32的地址是线性地址,而线性地址是属于虚拟地址的。

也有网友表示STM32没有MMU,是直接物理地址的映射,32只有MPU。在与网友交流并查阅相关资料后,我的看法是,不是所有的ARM都有MMU,也不是所有的32都有MMU,关于MMU与MPU,请阅读:ARMv7多进程基础之MMU篇

这里插入一个知识点:MCU和MPU的异同

嵌入式MCU单片机集成了嵌入式系统工作所需的逻辑计算内核cpu,存储数据/代码的RAM,EEPROM和Flash,内部互联总线—Crossbar、AMBA(APB、AHB以及AXI bus),定时器资源、中断控制器(INTC,通用GPIO,ADC、DAC和ACMP,段码LCD控制器、TFT LCD控制器,通信接口/控制器—IIC、SPI、USART、CAN等;

当然,MPU中也会集成很多嵌入式系统工作所需的大部分片上外设,但因为其计算单元CPU内核运行速度非常快,所以其一般不会再片内集成系统工作所需的RAM和Flash存储器,而是集成SDR/DDR2/3/4等外部SRAM扩展接口和NAND/NOR Flash扩展接口,用户设计基于MPU的硬件系统时还需选择合适的SRAM和外部Flash才可以保证系统正常工作。(参照电脑计算机)

相比较于MCU来说MPU多了两个部件, CACHE(高速缓存,是硬件)与MMU。(在我看来MCU是可以拥有MMU的,弄混淆的原因可能是MCU的界限越来越模糊),关于Cache请阅读:20 张图揭开内存管理的迷雾

还有网友表示,有MMU的芯片一般都不集成flash,都是外部flash加载到ram再到code总线。对于这点我的理解是上了操作系统后,对flash的容量要求变大了,内置flash比较贵,所以选择外接。

不知道我的理解有没有错误,如果有误的话希望网友指正,多多交流。

加载地址/存储地址和运行地址/链接地址

加载地址

加载地址是指代码存储所在的物理地址,由于ARM总是从0开始取值,即PC初始值为0,所以加载地址必须对应0地址,程序才能正确启动执行,之后才可以进行跳转,比如设置PC等于一个子程序的入口地址,而这个入口地址可能在rom中也可能在ram中。

运行地址

运行地址是链接器根据链接文件中指定的链接地址作为程序运行的起始地址,(作用)将程序中所有指令地址按照相对于这个起始地址的位置进行赋值,与程序实际运行时地址不一定相同,因为实际的程序运行起始地址总是0,而链接地址不一定为0,决定于程序需要在ROM还是RAM中运行);如果链接地址与加载地址设为相同,则所有寻址地址在ROM上都有对应的指令存在,也就可以正常执行;但如果链接地址与加载地址不同,则所有指令的地址是根据链接地址确定的,在ROM上不存在与之对应的代码入口(即使链接地址正好还处于ROM代码中,接下来执行的指令也将是不正确的和存在偏移的)

关于这些请查阅更多资料或阅读:关于加载地址和运行地址理解

存储地址就是MDK将程序下载到STM32芯片时写入的地址,一般都是片上FLASH的地址0x0800_0000。而执行地址则是程序运行的地址。对于STM32来说,绝大部分情况下,存储地址和执行地址都指向片上FLASH,因为片上FLASH即可用来存储程序又可用来执行程序。来自:STM32启动分析

MMU

关于MMU及更多请阅读:arm的mmu学习

什么是MMU

MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是*处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统。作用有两点,地址翻译和内存保护。MMU将虚拟地址翻译为物理地址。

描述一下处于CPU寻址的关键路径上的步骤:
1、CPU产生一条逻辑地址,送到MMU的地址翻译硬件中
2、操作系统设置段选择符,找到逻辑地址所属段的段描述符,判断相关属性权限
3、MMU中的地址翻译硬件根据段描述符中规定的段基址,加上逻辑地址中的Offset,转化成线性地址
4、MMU利用线性地址查询TLB
5、TLB命中则返回页表的内容(即某一物理地址),不命中则去主存中索引页表,如若主存中页表也不命中,去磁盘中查找。
6、根据页表内容,获取物理地址。

注:
每一个页表项中存储的是下一级页表的物理基地址。
每一个TLB项中存储的是页表项。

最后

更多阅读::STM32启动分析