抽象:地址空间

早起的操作系统其实只是一组函数(一个库),是在内存中运行的一个运行的程序,例如下图:

                             抽象:地址空间_第1张图片

其中的0-64KB,属于操作系统,包括代码、数据等等,64KB之后的属于运行中的进程;

后来,随着计算机的不断发展,人们开始想方设法的更有效率的使用计算机,也就是进入了多进程时代(multiprogramming),当时等等多个进程在给定时间内运行,例如当有进程在等待I/O,那么操作系统就会将CPU交给其他进程,这样可以让CPU被充分的使用。后来,人们使用的程序也在不断发展,就开始想要更好更快的机器,于是分时系统诞生了。具体地说,就是当时的许多人认识到了批量计算的局限性,尤其是对程序员本身,他们厌倦了长时间的低效率的程序编程-调试循环。交互(interactivity)变得重要起来,因为在那个时代,计算机还没有很普及,很多都是多个人,也就是一个小组、一个实验室,甚至一家公司只有一台计算机,于是就会出现许多个用户同时使用一台机器,每个人都在等待他的程序运行的情况。时分共享就是为了解决这个问题被提出。

时分共享就是让一个进程单独占用计算机所有资源,在很短的一段时间内,然后停止它,将它停止那一刻的运行信息保存在磁盘上,然后加载其他进程的运行信息,之后就对所有的进程重复这个过程,就实现了最简单的机器共享。但是,这种方法有一个大问题:太慢了!!!特别是随着内存的增长。:虽然保存和恢复进程状态很快,但是把内存的全部信息都保存到磁盘上就太慢了。因此,开发者更愿意将进程信息留在内存中,同时在它们之间进行切换,从而使操作系统能够有效地实现时间共享。

将进程信息保留在内存中是有效的提升了速度,但是也带来了安全性的问题,因为所有进程的信息都在内存中,很有可能会有某个进程不小心的误操作了其他进程的信息,当然恶意的故意去修改其他进程信息的情况也会出现。于是开发者们就必须开始考虑内存信息的保护问题。

于是地址空间(address space)的概念应运而生。地址空间就是内存的抽象,程序可以根据地址空间去找到对应的内存。一个进程的地址空间包含运行的程序的所有内存状态,例如程序的代码必须要在内存中,也就在地址空间里,当程序运行时利用栈保存当前的函数调用信息,同时分配空间给局部变量,传递参数和函数返回值,再通过堆来实现程序的内存动态分配,例如C++中的new。当然还有一些其他的东西,例如静态初始化的变量等等。

                              抽象:地址空间_第2张图片

上图就是一片地址空间,它总的大小为16KB,其中顶部是程序代码,占用大小为1KB,因为代码是静态的,在程序运行的过程中不会被改变,所以代码一般都会被放置在顶层。在代码后面,就是给运行中的程序分配的堆和栈,其中的堆放在剩余空间的顶部,栈放置在空间底部,这样可以允许他们同时增长,只需要他们增长的方向不同即可。当然这只是最初的堆和栈的一种分配方式,到了今天这样的分配方式早已经被优化掉了,例如对于多个线程的时候这样的分配就很难处理了。

到了今天,我们描述的地址空间 ,其实指的是操作系统提供给程序的抽象。因为程序肯定不是每次都被分配在0-16KB的内存中,而是有可能加载在任意的物理地址。但是对于程序来说,它只会认为它自己被加载在特定的地址中,一般为0。而当程序要去操作它认为的0地址的时候,实际上操作的物理地址可能是个任意的值,这就是内存虚拟化,也就是现代计算机系统的基础。只有有了虚拟化的内存,才可以良好的实现进程之间的隔离,就可以保护进程彼此之间的信息互不干扰,从而避免进程误操作与恶意破坏的问题的发生。

有一点要注意的是,在我们平时的开发中,可以通过各种各样的操作查看到自己使用的内存的地址,那么这个地址是真实的吗?当然不,通过代码所能看到的地址都是已经经过操作系统转换的虚拟地址,例如下面的代码:

抽象:地址空间_第3张图片

结果所输出的:

this is Main address :  0x55f1fef4097a
this is a address :  0x7fffd71859a4
这两个都是虚拟的内存地址,真正的物理控件地址需要经过操作系统与硬件翻译才能得到。

你可能感兴趣的:(操作系统)