分类:
2012-05-30 22:15:20
如前所述,UNIX进程空间分为两个部分,程序区和数据区(包括数据段、栈段和u变量/内核栈),它们的物理空间通常是连续的,但有时也不连续。标记SLOAD标明一个进程当前是在内存中还是在磁盘上。如果进程的proc.p_flag包含SLOAD标记,则它现在内存中,否则它在磁盘上。当换出内存中的一个进程到磁盘时,分两步操作:换出程序区和换出数据区。由于程序区可能和其他进程是共享的,所以在最后一个使用该程序区的进程被换出前,它是不会被换出到磁盘的。而数据区是每个进程独有的,所以它会无条件地被换出到磁盘上。
事实上,程序区的换出过程只是释放内存,而不像数据区的换出操作,把内容复制到磁盘上,因为程序区的内容是只读的,在进程运行期间不会改变,所以下一次再换入进程时,可以从磁盘交换区直接读出。磁盘交换区的程序段是在进程加载时分配并复制的。而数据区在进程运行期间是会改变的,所以必须要被复制到磁盘上,下一次进程在被换入时,再复制到内存中,这样进程才能够继续正确运行。
咋一想像,进程的交换过程似乎难以理解,一段正在执行的程序和数据,怎么能够被复制到磁盘上而且再拷回内存后还能够继续正确执行呢?事实上只要清楚进程运行的过程,就不难理解了。进程所执行的指令都存放在程序区,而指令所访问的全局变量(或从堆上分配的变量)在数据段,局部变量在栈段(或者寄存器中),进程上下文在u变量中。当交换器执行时,其他所有进程都被中断而处于内核态,并且它们当前执行的PC值(返回地址)都存放在各自的内核栈上,而栈指针存放在u.u_rsav、u.u_ssav或u.u_qsav中。这样只要保存了u、内核栈和栈段就保存了PC和局部变量值,而保存了数据段就保存了全局变量(或从堆上分配的变量)的值。至于进程所用到的寄存器值,它们的内容是不需要单独复制到磁盘上的,因为它们已经被保存到栈段和内核栈上。这样当进程被换出时,它所有的内容都没有丢失。
当位于磁盘上的换出进程再次被换入到内存中时,程序和数据被正确复制回内存中,且被映射到换出前的地址——虚拟地址,这一点至关重要!如果没有虚存机制,那么进程的交换几乎不可能实现,可以说它是进程交换的基石。因为再次分配到的内存物理地址很可能不是进程被换出前的物理地址,所以如果通过物理地址直接访问的话,那么进程的重新执行就完全错误了。当然有一个解决办法就是给程序区和数据区的各个函数和变量加上偏移实现重定向,但这样做显然很麻烦,而且性能很低(具体参见第11章《UNIX可执行文件》)。有了虚存机制,不管进程的程序和数据被复制到物理内存的哪个地方,它们都可以被重新映射到换出前的虚拟地址,而指令执行和数据访问都是基于虚拟地址,这样进程在被换入后,依然可以继续正确执行,就像它从来没有被换出过一样,进程换出换入后的内存映射情况如图10-2所示。好比DVD播放器的记忆功能,当我们在看碟片的过程中因为有其他事情把它暂停并取出,一段时间后把碟片再放回到播放器中,它依然可以继续暂停的位置播放。
图10-2 进程p1换出换入后的内存映射情况