现在我们了解了语言,也了解了CPU的运作原理。
我们现在开始尝试去编写一个程序。
而去编写程序就需要去了解内存的数据排布问题,因为高级语言是去操作内存的,而是不能去操作硬件的,硬件是需要汇编代码去操作的。
假设我先在要编写一个程序
那么这个程序会有
- 代码
- 数据
那么我们就需要在内存中开辟空间来存储这两个内容
存储数据的叫数据段,存储代码的叫代码段。
那么我们在执行程序的代码的时候,发现了一个问题,如果两个程序在系统中运行,如果不加以区分,程序A就会占用程序B的地址,并修改程序B的数据,那么这样的话,程序B就会出现问题,为了避免这样的问题,就需要将数据分为,
- 私有的数据
- 共享的数据
怎么才能实现私有的数据呢?
有两种数据排布:
- 按连续的结构进行分配
- 按不连续的结构进行分配
首先我们讨论一下,连续的地址和不连续的地址。
我们选择使用连续的地址进行分配
如果当指令A执行后,跳转到下一条指令B去执行,将上一条指令A执行的结果数据保存起来,然后当这个指令B执行完成之后,将执行B执行后的数据返回,而指令A的数据依旧没有被释放,然后继续执行指令B下一条指令。其中指令B被限制只有指令A完全执行完成之后,其操作的数据才可以进行访问。也就是说指令B是不能访问指令A的数据,因为指令A没有执行完成,而指令A也只能等到指令B结束之后才可以操作指令B的数据。
那我们就可以实现所谓的私有数据。
这个结构我们称为栈,其特点就是先进后出。实现栈结构最好的结构就是顺序结构,使用连续的地址空间。
而共享的数据,就是任何人都可以访问,这个结构我们称为堆结构。
那么执行指令时,需要给当前指令开辟空间,而多条指令组成的指令集所开辟的空间,我们成为栈帧。
栈帧中存储这,指令和指令的数据。
当一个指令集中的一条指令,需要跳转到另一个指令集,那么我们需要做标记,什么时候开始,到什么时候执行结束,回到当前指令集。
如上图所示
因为栈结果是先进后出,当指令集A执行后,指令集B进来,我们需要需要就需要将栈顶保存起来,以便于下一个指令集从哪里开始分配地址空间,而我们相对应的就需要保存栈底,当一个指令集执行完成之后我需要额外开辟一块空间来存储一下上一段指令集的栈底,等到当前指令集执行完,释放空间之后就会读到该地址,跳转到上一个指令集的跳转地址。
而这个栈顶就存在 栈顶寄存器中,而这个栈底 存在于 栈底寄存器中。
而我们还需要将需要执行的代码和数据加载进内存中,所以这部分内容也需要进行内存分配。
存放代码的地方叫做代码段,存放数据的地方叫做数据段。
现在我们引入一个软件:
这个软件他叫:操作系统
这个软件功能就是为屏蔽硬件而研发的。那么这个软件运行的时候也是需要缓存的,而且这个缓存比较独特,其他软件是不可以访问,是操作系统独有的。
那么在电脑启动时,他就要划去一部分空间,而剩余的空间才是用户才可以访问的地址空间。
当OS拿走一部分空间之后主存剩余的内存排布如下:
栈是自上而下的增长,而堆是自下而上的增长,利用栈底的特点,防止越界,访问到操作系统的内存。
现在我们为了更好的管理内存排布,我们将内存进行规整排列。
进行分段处理,而在4G内存未出现的时候,内存寻址还是1byte,当内存升级到4GB之后,内存寻址就变为 1MB= 64KB。
这是为啥是64KB呢?那是因为内存也在迭代发展,当内存可以存储到4GB之后,每取一次数据就被定义为了1MB,内存在想,我空间都这么大了,你还1byte的取这不是侮辱人么。所以我提升了,取一次数据为1MB。
那么一共有多少个段,1MB = 2 20 这也是 Intel在8086时使用20根地址总线的原因。
最多可以有2^16个段,而2^10=1K,所以最多可以分为64K个段。
因为储器1MB地址空间,所以每个段为1MB/64K=16B,即每个段均为16个字节。