系统总线
计算机系统总线分为数据总线,控制总线和地址总线.其中和内存相关的是数据总线和地址总线,有些cpu中,这两者甚至可以复用.
总线宽度
一条线同时只能传输一个bit,多条线就可以同时传输多个bit,有多少条线,叫做总线宽度.
总线带宽(即单位时间内可以传输的总数据数)为:总线带宽 = 频率 x 宽度 / 8,单位是字节/秒.其中频率是cpu的频率,比如1.5GHz.
而总线的宽度由CPU架构决定,也就是所谓8位,16位,32位,64位架构.64位的地址总线宽度就是64.64位的宽度,同时就可以传输64个bit(位).
寻址范围
在编程的时候接触的"内存"通常都是内存的地址,也就是编号.
CPU通过地址总线来传输内存的地址,64位总线一次可以传输64位的数据,也就是0000....0000(64位二进制) 到 1111....1111(64位二进制).
也就是说,64位的cpu它访问的地址,也就是2^64 这么多种组合,换成十进制就是0到2^64 - 1,必须在这之间,再多就不能访问了.
存储单元
内存的存储单元是字节,每个内存地址对应一个字节,也就是8位.两个连续的地址0x0001和0x0002之间隔了一个字节.
寻址空间
有了寻址范围,有了存储单元,那么寻址空间就是cpu能够访问的最大内存空间.
所以32位的寻址空间是2^32个组合 乘上 每个组合是一个字节,也就是2^32 字节,一个G是1024x1024x1024=2^30 ,所以32的寻址空间是2^32 / 2^30 = 4G,32位架构只能使用4G内存,再多也起不到作用.
内存地址
前面说到64位架构的寻址范围是64个0到64个1,所以内存地址就是64位二进制,换成16进制是16位.
虚拟内存
假如一台计算机的内存是2G.操作系统会给进程分配内存,不同的进程处在内存中不同的位置,叫做物理地址,并且还是动态的,非常复杂.
但是程序实际上并不关心物理地址,系统会给每个进程生成一份虚拟内存,这个虚拟内存的大小就是cpu架构的寻址空间,比如32位是4G.

虚拟地址
虚拟内存的起始和结束地址都是固定的,以32位为例,是0x0000 0000到0xFFFF FFFF.
进程中的数据,对应的是虚拟地址,而数据真实存在的地方,也就是物理地址,由操作系统来负责做隐射,进程无需关心.
通常虚拟内存是比真实内存大的,尤其64位的寻址空间极大,有没有内存可用,系统自己知道.
不同的进程管理各自的虚拟地址,也就防止了进程之间互相访问和修改对方的内存.
虚拟内存结构

这张图是x86的32位架构cpu虚拟内存结构.
每个进程都有一个虚拟内存,虚拟内存有很多分区,在上面这张图中,从下到上是内存低位到高位.
最高位的1G是内核空间,这部分的内容,每个进程都一样.
栈区位于比较高位,需要注意的是,特点是栈地址是由高向低分配,新入栈的数据,地址更低.栈区也是进程无法管理的.
堆区可以允许进程自由申请内存,是低位到高位增长的.
理论上堆区最高位到栈区最低位就是未分配的内存,可以由栈和堆随意使用,但是实际上栈区被限制只有几兆的大小,大部分未分配都是堆区.
内存分页
假设内存是连续分配的(也就是程序在物理内存上是连续的)
1.进程A进来,向os申请了200的内存空间,于是os把0~199分配给A
2.进程B进来,向os申请了5的内存空间,os把200~204分配给它
3.进程C进来,向os申请了100的内存空间,os把205~304分配给它
4.这个时候进程B运行完了,把200~204还给os,这5空间可能很长时间都会空着了.
因此系统必须支持不连续的内存分配.
但是不连续就得考虑效率问题,假如每个字节都这么精打细算,效率就太低了,因此现代CPU定义了MMU(Memory Management Unit 内存管理单元)的最小单元是4K,即4096个字节.叫做页.
MMU就负责将虚拟地址隐射到可能分布内存中各种地方的物理地址,一个进程被分割放在n个页中.
映射是一一对应的,虚拟内存中的4K,被映射到物理内存中的4K,虚拟内存中叫做页(page),物理内存中的叫做页帧(page frame),页和页帧是一样大的.这个隐射存储在内存里的页表中.
内存碎片
上面那个例子中,在分页机制中也会存在,只不过指定了最小单元后影响就小很多了,MMU的最小单元页的大小是4K,每个页里没用上的部分依然是内存碎片.
指针
指针是一个数据结构,它需要一块内存来存储,存储的内容,就是它所指向的地址.
所以指针存的也就是64位二进制,因此64位架构的指针所占的内存空间是8个字节.
这8个字节全部用来存储地址,也就是指针的值,至于指针的类型,是编译器的工作,并没有在内存中体现.
网友评论