分页的第一步要准备好一个页表,我们的页表是什么样子呢?现在我们要设计一个页表啦。
设计页表其实就是设计内存布局,不过在规划内存布局之前,我们需要了解用户进程与操作系统之间的关系。
前面讲保护模式时,我们知道,为了计算机安全,用户进程必须运行在低特权级,当用户进程需要访问硬件相关的资源时,需要向操作系统申请,由操作系统去做,之后将结果返回给用户进程。进程可以有无限多个,而操作系统只有一个,所以,操作系统必须“共享”给所有用户进程。它们的关系见图:
上图不仅展示了用户进程共享操作系统的逻辑依赖关系,还用插槽展示了它们的配合关系,用户进程要想完成某件工作,需要与操作系统结合在一起才行,那用户进程和操作系统它们是什么关系呢?
要完成一件事,用户进程做的事情只能算个半成品,您可以理解成:用户的代码加上所需要的操作系统中的部分代码才算完整的程序,为什么说是操作系统中的部分代码呢?原因很简单,因为操作系统严格来说是一套功能的集合,用户进程所需要的部分可能仅仅是其中的一小小部分,并不是所有功能都会用到。用户进程能用哪些功能,是由操作系统决定的,不是用户想用什么就用什么,而是操作系统提供什么它就用什么。完整的程序概念见图:
它和操作系统需要共同配合才能完成一件事,它们的关系有如服务提供商和客户的关系。服务提供商提供一些服务,客户只能用这些服务,也就是说客户依赖于服务提供商提供的服务项目,是服务提供商主导客户。比如咱们在网上买东西,咱们只需要挑选好商品后写好地址,然后下订单就成了。这事完了吗?必须没有,得拿到商品才算完事。所以,之后的事情就交给电商了,他们为你从库中挑选商品,然后用物流送到您家,这才拿到了商品,到此才算完事了。
以上购物的例子就是典型的用户程序和操作系统的关系,咱们挑商品下单这件事就相当于进程,而网上的电商才是充当了操作系统的角色,根据买家的需求找到所需要的资源,然后通过物流,将商品(结果)返回。
上述所说的用户进程和操作系统的关系,都是基于用户进程共享操作系统。我们设计的页表也要满足这个基本要求:共享。
如何在页表中实现共享呢?这个简单,只要操作系统属于用户进程的虚拟地址空间就好了。
说起来简单,这该怎么做呢?我们可以把4GB虚拟地址空间分成两部分,一部分专门划给操作系统,另一部分就归用户进程使用。比如我们之前都听说过,操作系统在4GB内存的高地址,用户进程在4GB内存的低地址。比如linux,它就运行在虚拟地址的3GB以上,其它用户进程都运行在3GB以下。
页表的设计是要根据内存分布情况来决定的,我们也学习linux的作法,在用户进程4GB虚拟地址空间的高3GB以上的部分划分给操作系统,0~3GB是用户进程自己的虚拟空间。为了实现共享操作系统,让所有用户进程3GB~4GB的虚拟地址空间都指向同一个操作系统,也就是所有进程的虚拟地址3GB~4GB本质上都是指向的同一片物理页地址,这片物理页上是操作系统的实体代码。实现起来也比较容易,只要保证所有用户进程虚拟地址空间3GB~4GB对应的页表项中所记录的物理页地址是相同的就行啦。哈哈,这句话确实有点长,我自己也反复断句了几次,不过这个在加载用户进程时咱们再细说,在此我们只需要完成内存空间划分就行了。
以上我们讨论的结果是:虚拟地址空间的0~3GB是用户进程,3GB~4GB是操作系统。
本节内容摘自《操作系统真象还原》