• 2005-12-20

    管窥OS——进程透明化

    Tag:向下走

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/1727432.html

    在软件设计中,一个基本原则就是“高内聚,低耦合”。作为一类历史悠久的经典软件,OS历经多年的发展,在设计上已经达到了相当的高度。随便翻开一本操作系统的教材,那些经典词汇便不断的在我们面前闪烁:进程、内存管理、文件系统、输入输出等等,而它们正对应中设计中的一个个模块。记得初学OS的时候,我怎么也想不明白这些东西究竟有什么联系,这一方面体现出我对这些内容不甚了解,另一方面也充分体现出了设计的精彩,模块之间耦合极低。只可惜,我的老师从来没有站在设计的角度,为我们讲解OS。我们不妨从OS这个大拼盘中取出一块,管窥其中精彩,讨论的目标就锁定于IA32架构。

    在现代OS中,在用户的角度来说,计算机同时干好几件事似乎是已经成了一件理所当然的事情,而这一切源于多进程技术的存在,正是多进程技术善意的欺骗,让感观上的“同时执行”成为了可能。其实,上当受骗的不只是用户,每个进程也是受害者,他们认为单纯的认为自己独霸着CPU,殊不知,这一切都是多进程技术为它们营造的虚拟空间。顺便说一下,虚拟这个词可谓应用广泛:网络为用户营造了虚拟世界,虚拟机让跨平台切实走入程序员们日常生活……

    CPU很单纯,它所做就是“取指、执行”这样简单的重复性工作。既然各位都是好人,那唱白脸的活只能交给中间人——OS来做了。那我们就来看看OS究竟做了怎样的手脚,营造出这样一份欣欣向荣的景象。

    老实的CPU只懂得按照一个预先设定运作模式运行,比如认定CS:EIP所放的就是自己要执行指令,比如认定ESP就是自己的堆栈。这就给了OS可乘之机,只要能够入乡随俗,就可以对欺骗CPU,比如改变CS:EIP就可以改变CPU所要执行的指令。偶尔,CPU自欺欺人,比如中断发生时,它在堆栈中保存了CS:EIP,执行中断处理程序,之后,再从堆栈中恢复过来,继续未尽的事业。正是有了这样的机会,进程调度才可能起作用。

    谈到这里,我想就中断再说上一些。在我看来,中断是OS的动力源。前面的讨论中,我们也知道了,CPU太过老实,只知道一条道跑到黑,正是中断的存在才让这个世界变得丰富多彩。其实,如果把中断这个名字换成事件,对程序员来说,或许会更熟悉一些,就是兵来将挡的道理。我们已经面对过太多的事件处理机制,GUI有之,Web页面有之,即便是服务端应用本质上也是一种事件处理。其实,软硬件本身就有许多相同的地方,差别只在谁来做而已,中断这档事交给了硬件。分清责任在设计中非常重要,在OS这个层面上,我们应该弄清楚,哪些东西硬件能做,而哪些需要OS亲历亲为。

    在执行过一条指令之后,CPU会去看看是否有中断发生,如果有,就是中断处理程序表演的时间了。很多情况下都会产生中断,而这里我们关注的是定时器,因为通常进程调度就是在这个时候起作用的。因为CPU是在堆栈中保存的CS:EIP,所以,只要改变ESP,傻乎乎的CPU在恢复的时候,取到的就是另外的CS:EIP,而它对此一无所知,殊不知,此时早已物是人非了。人们常说的进程切换做的就是这样的事情。于是,OS得以成功的欺骗了CPU。

    骗了CPU,OS还要摆平另外一边——进程。进程是一个运行起来的程序,程序是什么。大多数情况下,我们都在用各种高级语言写程序,经过编译器的处理之后,它们就变成了一条条的指令。最终完成任务就是靠CPU把这些指令一一执行。OS欺骗进程的手法就是保证这些指令的正常执行。说起来很简单,但别忘了,世界上还有进程切换这么一回事。如果进程被拦腰斩断,当它有了翻身做主的哪天,一切还要变回原来的样子才可以。这就涉及到一个问题,如何恢复进程原有的风采。这是一个持久化的问题。应用级程序员对于持久化应该并不陌生,它解决实际上就是恢复的问题,不同的只是存放信息的位置,通常的选择是外存,比如硬盘,而在进程切换时的选择通常是内存,一般是堆栈。

    找到了存放地点,接下来还要考虑一下存放的内容。程序=数据结构+算法,对于一个程序而言,对它持久化,关键就在于数据结构的持久化,算法本身持久化是没有意义的,因为只有数据算得上是物理的存在,这个东西想来是很奇妙的。如果把算法比作精神,那数据就是物质。怎样保存精神的东西呢?归根结底,还是要通过物质的东西来体现,就像我们把自己的想法记在纸上一样。在CPU运行的层面上,数据就是各个寄存器的内容。所以,只要把寄存器的内容保存下来,我们便得以将进程持久化了,当然,这还会涉及到一些内存里的东西,但其核心在于数据持久化。

    还有一个有趣的地方,持久化的工作是在原子操作执行完毕之后进行的。对于CPU来说,原子操作应该是一条指令,值得注意的是,CPU响应中断是在一条指令执行完毕,而不是过程之中,这样就保证了在中断处理过程中,原有指令执行是完整的,这与数据库中的原子性异曲同工。持久化的工作只有在一个原子操作之后进行,才可以保证数据恢复时的正确性。在OS层面上,正是由于原子性的粒度很小,即指令级,这样才使得进程管理部分有机会在这种级别上进行调度,而在高级语言上看,程序是在任意处中断执行的,因为通常高级语言的一条语句要对应着多条指令,而这恰恰成为了进程透明化的又一关键。

    就这样,OS成功的做到了欺上瞒下,实现了进程的透明化,正是因为这样,我们在使用进程时才可以完全的一意孤行。这就是设计的精彩所在,其它模块对其背后的复杂一无所知,而这只是OS经典设计的一点体现。

    分享到:

    历史上的今天:

    不枉走一遭 2009-12-20
    引用地址:

    评论

  • 写得比较通俗,说明作者对OS看得比较透,呵呵。
    只是“CPU很单纯,它所做就是“取指、执行”,似乎改成“取指、分析指令,执行指令”更确切,通俗之余别忘了严谨。
  • 缺页中断貌似是可以发生在一条指令执行过程中的
    回复huangyi说:
    对啊!貌似。内存管理也有着与进程类似的透明化。
    2005-12-22 16:26:04