“计算机科学中的任何问题,都可以通过加上一层逻辑层来解决”

―――――――计算机科学家 David Wheeler

目前,形形×××的软件,层出不穷,可是他们都脱离不了一个基础,那就是:计算机硬件系统。也就是cpu,内存和各种IO接口,以及连接他们,使他们之间相互通信的路径,也就是电路。cpu内部是大量的集成逻辑电路,cpu不断受到一种电信号的“刺激”,这种刺激经过cpu内部的逻辑电路的一层层的传递转换,最终输出另一种电信号。这种输入,输出动作,是有一定的逻辑的,而不是乱七八糟。通过编写汇编代码,可以实现对cpu内部逻辑电路的刺激,并引起一系列的逻辑输出。将这些逻辑,映射到人们所能理解的知识上,比如输出1代表对,输出0代表错误,或者如果输出1,则继续刺激,如果输出0,则停止刺激,等等等等,这样就构成了从基本的逻辑电路,到复杂的思维逻辑的映射,由简单逻辑的层层嵌套,构成了复杂逻辑。将汇编语言,用人类容易理解的语言抽象出来,就形成了高级语言。将高级语言的意思,转换成低级语言的过程,叫做编译。就像我说:冬瓜,而用汇编语言表示冬瓜这个意思,就是:“撇,横折,捺,点,点,撇。。。。。。。。”。

我们知道,最纯种的计算机系统,其实是没有操作系统之说的,因为操作系统本身也是程序。操作系统就是一种可以提供给其他程序方便编写并运行的程序。由程序来运行程序,而不是由程序自己来运行,这是操作系统提供的一种虚拟化表现。对于纯种计算机来说,他只能允许执行一个任务,整个机器只能被这个程序独占。比如开机,从软盘上(可以设置从哪里开始执行)执行程序,直到执行完毕,或者人为中断,执行完后,拿出软盘,再插入另一张软盘,重新载入,执行另一个新的程序。再执行这个程序的过程中,不允许任何人打断,一旦意外终止,就要重新运行。早期的计算机,都是这种形式,也叫做单任务性。早期的计算机,确实就是这么用的,如果有10个人要用,比如第一个人拿着他的软盘,上面有一道数学题,他插入软盘,然后重启机器,机器从软盘特定的扇区载入程序代码执行,结果显示在显示器上,比如这个程序2个小时运行完毕,第一个人从显示器上抄下结果,走了。后面有9个人在排队等待用计算机。然后第二个人同样拿着他的软盘,插入软驱,重启。。。。。。。。每次更换程序,都需要重新启动机器,简直就是梦魇。再者,如果某个程序运行期间,会有空闲状态,则其他程序照样也不能使用cpu,cpu只能在那里空振荡。为了解决这两个问题,操作系统出现了。操作系统本身也是一段程序,机器加电之后,首先运行操作系统这个程序,但是操作系统这个程序,他可以随时载入其他程序执行,也就是说它可以随时从软盘上读取其他程序的代码,并让cpu执行,但是每次也总是要等待这个程序执行完毕,才能接着载入下一个程序执行。当被载入的程序执行的时候,不能做任何其他的事情,包括操作系统本身的程序模块,但是任何产生中断的事件,可以中断正在运行的程序。程序执行完毕之后,会将cpu使用权归还操作系统,从而继续操作系统本身的继续运行。这种操作系统称为单任务操作系统,典型代表,就是dos。一旦在dos种载入一个程序执行,如果没有任何中断事件发生,则这个程序就独占cpu,执行完毕之后,会回到dos操作系统继续运行。经过这样的解决,执行多个程序,期间就再也不用重新启动机器了。第一个问题解决了。在这个基础上,操作系统可以将多个程序一个接一个的排列起来,成批的执行,中途省掉了人为载入程序的过程,这个做法叫做批处理。批处理操作系统,相对于单任务操作系统来说,可以顺序的,无需人工干预的批量执行程序,比简单的单任务操作系统又进了一步,但是其本质还是单任务性,即一段时间之内,仍然只会观察到一个应用程序在运行,仍然只是一个程序独占资源。再后来,操作系统针对系统时钟中断,开发了专门的中断服务程序,也就是多任务操作系统中的调度程序。时钟中断到来的时候,cpu根据中断向量表的内容,指向调度程序所在的内存地址入口,执行调度程序的代码,调度程序所作的就是将cpu的执行跳转到各个应用程序所在的内存地址入口。每次中断,调度程序以均等的几率,指向不同程序的入口,这样,就能做到极细粒度的应用程序入口切换。比如每10ms中断一次,那么也就是说每个应用程序,可以运行10ms的时间,然后cpu运行下一个程序,这样依次轮回。微观上,每个程序运行的时候,还是独占cpu,但是这个独占的时间非常小,通常10ms,那么一秒众就可以在宏观上“同时”运行100个程序。这就是多任务操作系统。多任务操作系统的关键,就是具有多任务调度程序。

上面说了计算机系统,操作系统。为什么要说他们呢?我们说的是虚拟化啊。呵呵,其实计算机系统从诞生的那一天开始,就在不断的虚拟化,硬件逻辑被虚拟化成汇编语句,汇编语句再次被封装,虚拟化成高级语言的语句。高级语言的语句,再次被封装,形成一个特定目的的程序,或者称为函数,然后这些函数,再通过互相调用,生成更复杂的函数,再将这些函数组合起来,就形成了最终的大应用程序。程序再被操作系统虚拟成一个可执行文件。其实这个文件代表了什么呢?到了底层,其实就是一次一次的对cpu的电路信号刺激。也就是说,硬件电路逻辑,一层层的被虚拟化,最终虚拟化成一个程序。程序就是对底层电路上下文逻辑的另一种表达形式。

那么虚拟化的好处是什么呢?显而易见,虚拟化将下层的复杂逻辑,转变为上层的简单逻辑,方便人类读懂,也就是说“科技,以人为本”。任何技术,都是为了将上层逻辑变得更加简单,而不是越变越复杂,当然使上层越简单,下层就要做更多的工作,就越复杂。

整个计算机技术,从开始到现在,就是一个不断的抽象,封装,虚拟,映射的过程,一直到现在还在不断抽象封装着,比如近年来兴起的java等等语言,它比c的抽象封装度更高,当然使用起来也比c方便核简单多了,但是随之而来的,其下层就要复杂一些,所以java代码一般运行速度慢,耗费资源也大,但是对于现在飞速发展的硬件能力,应该不成问题。

同样,cpu也不仅仅只是一味的增加晶体管数量这么简单。cpu制造者也在想尽办法将一些功能封装到cpu的逻辑电路中,从而出现了更多的指令集,这些指令集就像c函数一样,不必理解它内部到底怎么实现的,只需要发给cpu,cpu就会启动逻辑电路计算。到目前为止,intel的cpu已经发展到了酷睿2代双核。主频1。6G的酷睿双核cpu,竟然性能比主频3G甚至超频到4G的奔腾4代cpu还高。所以,cpu的设计主要是内部逻辑的优化,抽象封装,而仅仅提高主频的话,会阻碍cpu的发展,当然主频越高越好了。

其实虚拟化的思想,在计算机的各个方面,都是存在的,比如经典的OSI模型,就是一个不折不扣的抽象虚拟模型。尤其是tcpip协议给上层抽象出来的socket接口,意即“插座接口”,完全可以理解为,只要将插头接上这个插座,就会和网络接通,使用网络,而不必管它是怎么实现的,就像将交流电插头插入市电插座一样,插上就获得了电压,而不用管市电电网的拓扑,更不用关心国家总电网的拓扑了。

上面说了很多关于汇编和操作系统的虚拟抽象,下面我们就来说说计算机系统中的存储子系统中的虚拟化。

存储子系统的元素包括:磁盘,磁盘控制器,存储网络,磁盘阵列,卷管理层,目录虚拟层,文件系统虚拟层。我们就从下到上,一一描述这几个元素,看看存储子系统是怎么抽象虚拟的。

文件系统。上面讲了磁盘怎么存储数据,和怎么让磁盘存储数据。而数据只是存储上就完了么?显然不是。打个比方,有个记者,早晨出去采访,手中拿了一摞纸,他每看到一件事,就记录下来。对于“怎么将字写在纸上”这个问题,他是这么解决的,他用笔在格子上写字,写满一行再写下一行,还不够就换一张纸。对于“怎么让自己在纸上写字”这个问题,是他自己通过大脑(控制器),通过神经网络(scsi线缆),操纵自己的手指(磁头臂),拿着笔(磁头),看见有空的格子,就向里写。这两个问题都解决了。可是这一天下来,他回去想看看一天都发生了什么,他拿出记录纸,却发现,信息都是零散的,本无法阅读,有时候读到一半,就断了,显然当时是因为格子不够用了,写到其他地方了,造成了信息记录的不连续,有的地方还有删除线,证明这一块作废了,那么有效的记录到低在哪里呢?记者方寸大乱,数据虽然都完好的记录在纸上,但是他们都是不连续的,凌乱的,当时是都记下来了,但是事后想要读取他们,就没辙了。磁盘记录也同样,只解决磁盘怎么记录数据和怎么让磁盘记录数据,是远远不够的,还应该考虑“怎么组织磁盘上的数据”。我们还是拿这个记者的例子来说明。我们都能想到,将凌乱的记录组织成完整的一个记录,只需要在相应的地方做一下标记,比如“此文章下一段位于某某页,第几行”,就像路标一样,一次一次的指引你最终找出这个完整的数据,这个思想,称为“链表”。如果将这个链表单独的做成一个记录,存放到固定位置,每次只要参考这个表,就能找出一条数据在磁盘上的完整分布情况,利用这种思想做出来的文件系统,比如ms的fat文件系统,它把每个完整的数据称为文件,文件可以在磁盘上不连续的存放,由单独的数据结构来描述这个文件在磁盘上的分布,这个数据结构就是文件分配表,file allocate table,也就是fat的由来。或者用另一种思想来组织不连续的数据,比如ntfs,他是直接给出了一个文件在磁盘上的具体扇区,开始--结束,开始--结束,用这样的结构来描述文件的分布情况。文件系统,将磁盘抽象成了文件柜,同一份文件可能存放在一个柜子的不同抽屉中,利用一份特别的文件来记录“文件--对应抽屉分布情况”,这些用来描述其他文件分布情况及其属性的文件,称为元文件,metadata。元文件一般情况下要存放在磁盘的固定位置,而不能将其分散,因为最终要有一个绝对参考系统,但是其他一些文件系统,甚至将元文件也可以象普通文件一样,在磁盘上不连续的分布,但是我们说过,一定要有一个绝对参考系统,也就是固定的入口,所以这些特殊的文件系统,其实最上层还是有一个绝对参考点的,这个参考点将生成元文件在磁盘上的分布情况记录,从而定位元文件,再根据元文件,定位数据文件,这样一层一层的嵌套,最终形成文件系统。 最终一句话,文件系统是对磁盘块的虚拟,抽象,组织和管理。用户只要访问一个个的“文件”,就等于访问磁盘扇区了。而访问文件,这个动作是非常容易理解的,也是很简单的,用户不必了解这个文件最终在磁盘上是存放到哪里,怎么存放的,怎么访问磁盘来存放这个文件,这些统统都是由文件系统和磁盘控制器驱动程序来做。

磁盘控制器。磁盘控制器的工作就是根其据驱动程序发来的磁盘读写信息,向磁盘发送scsi指令和数据。这个部件似乎没有什么可抽象虚拟的东西,其实不然。这一层上可以大作文章的。比如,raid。raid原理我们就不多说了,具体请参考笔者其他文章。磁盘控制器,完全可以对其驱动程序隐藏其下挂的物理磁盘,而虚拟出一个或者多个虚拟磁盘。由控制器来完成虚拟磁盘和物理磁盘的映射和抽象虚拟。raid就是一个点型代表,控制器将物理磁盘组成raid group,然后在RG的基础上,虚拟出多个LUN,通告给主机驱动。完成这种虚拟抽象的功能,需要raid模块的参与,要么将raid模块做在控制器硬件上,要么将其做成操作系统卷管理层的一个模块,都是可以的。有时候做在操作系统中,甚至其性能比做在硬件上更高,原因可能是主机的cpu能力比控制器上的cpu处理能力强,代码比控制器上的代码优化所致。

存储网络。早期的存储子系统,没有网络化,而目前的存储系统,网络化已经非常彻底。从磁盘到磁盘阵列控制器,从盘阵控制器到主机总线适配器,都已经嵌入了网络化元素,比如使用fc协议,或者tcpip协议,sas协议,甚至infiniband协议等等。那么在这一层上,有什么可以抽象的么?网络化只是为部件之间提供了一种可扩展的通路而已啊,实则不然,这一层也是有所深究的。我们要了解,在交换式san中,不管是基于tcpip协议的还是基于fc协议的san,网络中的任何节点,都是通过交换设备来互相通信的,是节点间通信的必经之路。如果在交换设备上做点手脚,就完全可以达到虚拟化的效果。要抽象业务逻辑,那么一定要理解他所提供的上层业务逻辑,所以我们可以在fc交换机或者以太网交换机上,嵌入scsi协议感知模块。比如某个N节点向另一个N节点report LUN的时候,交换机收到这个frame,则可以感知这个N节点的LUN信息,如果此时网络中还有另一个节点的LUN信息,则可以在交换机这一层,达到这两个节点的LUN的镜像,也就是说,scsi发起设备向目标设备传输的数据,经由交换机的时候,交换机内嵌的业务模块,会主动复制对应的帧到另一个节点的LUN上,让这两个LUN形成镜像,当其中一个节点故障的时候,交换机因为知道此时还有一个备份镜像LUN存在,所以并不会向发起者通告故障,而是默默的将发起者的数据重定向到这个镜像的LUN,发起设备并不会感知,这样,就达到了基于网络层的虚拟化抽象。当然,网络层的虚拟化,并不止是镜像,还有比如将某些N节点的LUN合并成一个池,然后动态的从这个池中,动态的再划分出虚拟LUN,向发起者报告等等。基于这些思想,已经开发除了智能fc交换机,但是基于以太网交换机的存储虚拟化,并没有见过有什么产品。

磁盘阵列。磁盘阵列可以说本身就是一个小计算机系统(JBOD除外),这个系统五脏俱全,是对存储子系统的抽象虚拟化最佳的表现。磁盘阵列,简要的说,就是一种将大量磁盘进行组织管理,抽象虚拟,最终形成虚拟的逻辑磁盘,最后通过和主机适配器通信,将这些逻辑磁盘呈现给主机。这个功能和上文提到的磁盘控制器的功能类似,但是磁盘阵列能提供比狭义的磁盘控制器更多的特色功能,况且简单的插在主机IO总线上的那种raid磁盘控制器,其接入磁盘数量有限,功能也有限。大型磁盘阵列,有自己的控制器,有的利用嵌入式技术,将特别定制的操作系统及其核心管理软件嵌入芯片中,来管理整个控制器并实现其功能;有的则干脆利用现成的主机来充当盘阵控制器的角色,比如IBM的DS8000系列盘阵,内部就是用的两台P570小型机作为其组织管理磁盘的控制器,其上运行aix操作系统,和相应的管理软件。不管是嵌入式,还是主机式的,盘阵控制器所担任的角色都是类似的。这个中心控制器,不是直接参与连接每块磁盘的,中心控制器利用其“抓牙”来管理下挂的磁盘,由抓牙再向“头目”汇报。这些抓牙,就是由中心控制器驱动的二级磁盘控制器,这些控制器作为中心CPU的IO适配器,直接控制和管理物理磁盘,然后由中心控制器统一实现raid,卷等高级功能(有些盘阵则可以将简单的raid功能直接下放给二级控制器来做)。抓牙和中心控制器CPU(其实就是核心管理软件)之间通过pcix总线或者pciE总线相连接实现通信。中心控制器对这些磁盘进行虚拟抽象之后,通过前端的接口,向最终使用它的主机进行通告。中心控制器不但可以实现最基本的raid功能,而且可以实现很多高级功能,比如LUN镜像,块照,远程复制,CDP数据保护,LUN再分配等等功能。在磁盘阵列上实现虚拟化,是目前最广泛的一种存储系统虚拟化形式。

卷管理层。狭义的卷管理层,是指运行在主机上的一个模块,负责卷的收集和再分配。广义的卷管理,可以包括运行在磁盘阵列上的LUN再分配模块,因为LUN对于盘阵控制器来说就是一个卷。一般情况下,说到卷管理,VM,都是指运行在应用主机上的功能模块。经过虚拟化之后的LUN,提交给主机使用,主机此时可以对这些LUN进行再次抽象和虚拟,也就是重复虚拟化(下面会提到),比如对其中某两个LUN进行镜像处理,或者对其中的某几个LUN,做成一个软raid系统。或者将所有LUN合并,形成一个大的资源池,然后象掰面团一样掰成多个LUN,这个过程和磁盘控制器或者盘阵控制器所作的虚拟化动作类似,但是这个动作是在主机上实现的,也就是卷管理模块上实现的。点型的卷管理软件比如默认就运行在aix操作系统上的LVM,或者第三方的软件,比如Veritas公司的VxVM。优化很好的vm软件,其性能有时候能超过硬件实现的raid。

目录虚拟层。不管是windows系统,还是unix系统,linux系统,其内部都有一个虚拟的目录结构。在linux下叫做vfs,即virtual file system。虚拟文件系统,顾名思义,这个文件系统目录并不是真实的,而是虚拟的。任何实际文件系统,都可以挂载到这个目录下,真实fs中的真实目录,被挂载到这个虚拟目录下之后,都成为了这个虚拟目录的子目录。这样做的好处是灵活性非常强。其次,操作系统目前处理外部设备,一般都将其虚拟成一个虚拟文件的方式,比如一个卷,在linux下就是/dev/hda这种文件。对这个文件进行读写,就等于直接对设备进行读写了。

存储子系统的虚拟化,可以在磁盘--网络传输--控制器---网络传输---主机总线适配器控制器---卷管理层---文件系统层---最终应用层的各个环节的虚拟抽象工作,使得最终应用软件,只要通过文件系统访问文件,就可以做到访问最低层的磁盘一样的效果。有时候还可以重复虚拟化,比如有一个设备,其后端是各种磁盘阵列,其前端是应用主机,本来,主机可以直接连接到各个阵列上面,但是中间如果再插入这么一个中介设备,收集各个盘阵提交的已经经过虚拟化抽象处理的LUN,然后自己再次对这些LUN进行再分配虚拟化,提交给主机,这就是重复虚拟化,这么做的一目的,就是使得后端的各种不同厂家的阵列的资源,整合起来,统一分配,增加可扩展性和方便管理。IBM的SVC系统就是这么一个设备,它是由两台suse linux server作为中心控制器,作为前后端的中介,在linux下运行管理软件,实现虚拟化。

关于带内虚拟化和带外虚拟化。所谓带内,即in band,是说控制信令和数据走的是一条路线。所谓控制信令,就是说用来控制实际数据流向和行为的数据。点型的控制信令,比如IP网络中的各种IP路由协议,他们利用实际数据线路进行传输,从而达到各个设备之间的路由统一,这就是带内的概念。带外,显然是说控制信令和收据走的不是一条路,控制信令走单独的线缆,受到“优待”。带内和带外,只是一种叫法而已,在7号信令中,是用共路和随路来描述的,共路信令指的是控制信令和数据走相同的线路,随路则指二者走不同的线路。随路,又可以称作“旁路”,因为他是单独一条路。明白了这个概念,我们就可以理解所谓“带内虚拟化”和“带外虚拟化”的概念了。带内虚拟化,就是说进行虚拟化动作的设备,是直接横插在发起者和目标路径之间的,斩断了二者之间的通路,执行中介操作,发起者看不到目标,而只能看到经过虚拟化的虚拟目标。带外虚拟化,则是在这个路径旁另起一条路径,也就是所谓的旁路,用这条路径来走控制信号,而数据信号还是可以由发起者直接走向目标,但是这些数据流是受控制信令所控制的,也就是发起者必须先“咨询”旁路上的进行虚拟化的设备,经过“提示”之后,才乖乖的目标之间走数据。

虚拟化无处不在,计算机科学需要虚拟化。