An Overview of Singularity Project [1-8] Tony Qu翻译 [原稿]

Singularity项目总揽

Galen Hunt, James Larus, Martín Abadi, Mark Aiken, Paul Barham,

Manuel Fähndrich, Chris Hawblitzel, Orion Hodson, Steven Levi, Nick Murphy,

Bjarne Steensgaard, David Tarditi, Ted Wobber, Brian Zill2

 

Microsoft Research

One Microsoft Way

Redmond , WA 98052

http://research.microsoft.com/os/singularity

Microsoft Research Technical Report

MSR-TR-2005-135

 

概要:

Singularity是微软研究院的研究项目之一,该项目来源于这样一个问题:“如果一个平台从一开始就是以可靠性为主要目标,它将会是怎样一个平台?”为了回答这个问题,研究人员正通过先进的编程语言和工具开发一个新的系统架构和一个崭新的操作系统(它的名字叫Singularity),目的就是为了制造出一个更加健壮和可靠的软件平台。在Singularity中,一些新技术和架构思想得到了实践,而这些技术和思想就是为了构建更健壮和可靠的系统而设计的。

 

1. 介绍

       软件是在系统平台上运行的,在过去的40年中系统平台不断发展,并显得越来越不堪重负。平台是一大堆代码的集合,包括操作系统、编程语言、编译器、代码库、实时运行系统、中间件等等,并且在硬件的配合下,一个程序才能运行。一方面,这样一个平台无论在盈利还是在应用方面都是一个巨大的成功,它形成了一个179亿美元的软件包产业,并且使英特网这样的革命性创新成为可能;另一方面,运行在该平台上的软件并没有许多用户和开发人员想象中的那样健壮、可靠、安全。

       这个问题的部分原因在于,我们目前的平台并没有发展到能够绝对超越上世纪六七十年代的计算机架构、操作系统和编程语言的水平。那个时期的计算环境和现在有很大的不同——计算机受到速度和内存容量的束缚;只有很少一部分技术人员和普通用户能够使用计算机;很少有计算机能够联网或连接物理设备。虽然这些那个时期的特征已都成为了过去,但现在计算机架构、操作系统和编程语言还没有发展到能够在计算机和它的使用上产生根本性转变的地步。

       在计算机的高速发展中,硬件的发展通常会引起系统和应用的根本性转变。相比之下,软件的发展速度要慢得多,很少能够产生根本性的进步。但不管怎样,软件确实发展了,它的发展不得不让我们重新思考一些旧的设想和应用。先进的编程语言、运行时系统和程序分析工具为构建更可靠和健壮的架构和系统打下了基础。

l         Java, C#这样的具有很强表达能力的安全编程语言。类型安全性(Type Safty)能够确保一个值或对象能够被正确解释和修改。内存安全性(Memory safty)确保一个程序仅能够访问内存中现在可用的部分。

l         优化编译器和高性能的运行时系统能够生成与不安全代码(Unsafe code [参考20])运行一样快的安全代码(safe code)。这些编译器不同于通用JIT(Just-in-time)编译器,它们会自顶向下地减轻与安全有关的操作的风险,从全局角度做优化。这些系统中的垃圾收集器能够简单快捷地把系统操作所使用的内存收回。

l         验证技术能够确保编译器、已编译代码、运行时系统的端到端(end to end)类型安全。类型化的媒介和汇编语言会对每个操作进行验证,判断是否是系统部件的动作,并保证语言的安全性,这些特性奠定了系统能够正确运行的基础。

l         可靠、规范驱动的缺陷检测工具确保了系统许多方面的正确性。一个可靠的工具能够找到检测到任何错误的发生,[along with false positive],当某一个缺陷被去除后,该工具能够检测到这个变化。规范驱动的工具并不是为了寻找特定的缺陷,应该说它是可扩展的,能够检查很多程序和特定库提取是否正确。

基于这些优点的语言和工具可用于探测和防止编程错误。目前,当系统架构的重大改变时,如何能够保证仍然能够进行错误检测还没有很好的解决方法,如果找到这样的方法,将对防止和减轻软件缺陷做出巨大贡献[参考28]

       本文还会详细介绍Singularity系统。第2章是该系统和它的卓越性的介绍。第三章介绍了Singularity系统的架构,突出介绍内核、进程和语言运行时系统。第四章将介绍该系统支持的编程语言。第5章将介绍输入输出系统和安全系统。第0章提供了一些系统的测试报告,附录A是内核ABI访问列表

 

2 Singularity系统

Singularity是一个全新的操作系统,目前正在开发中,它是基于更可靠的系统和应用程序的概念开发出来的。该系统利用了编程语言和工具的优势,试图开发出一个能够让软件正确构建和运行的环境来,在该环境中,程序动作更容易被验证,包括运行时错误也能够被验证。

       Singularity的一个重要特点之一是有一个基于独立软件进程(SIPs)的扩展模型。独立软件进程可以封装一个应用程序或者一个系统,它能够提供信息隐藏功能、失败隔离(failure isolation)功能和强大的接口。SIPs贯穿于整个Singularity操作系统和应用软件中。我们相信基于这种抽象概念的系统能够使软件更具可靠性。

       SIPsSingularity上的系统进程,所有内核以外的代码都在SIPs中执行,SIPs在很多方面与传统的操作系统进程不同:

l         SIPs是封闭的对象空间(object space),而不是地址空间。

l         SIPs是封闭的代码空间(code space)一个进程不能动态加载或生成代码

l         SIPs不是通过内存管理硬件实现分离的,多SIPs会驻留在物理或虚拟地址空间中。

l         SIPs之间的通讯是通过双向的、强类型化的、高度有序的通道实现的,每个通道会指定它的通讯协议和值传递方式,并且这两种方式都是需要验证的。

l         建立SIPs消耗很少的系统资源,SIP之间的通讯仅仅消耗很少的管理资源。如此低的资源消耗成本,促使SIPs成为理想的隔离和扩展机制。

l         SIPs是由操作系统创建和终止的,在终止SIPs时,它所占用的资源会被高效回收

l         SIPs是独立运行,即使对于不同的数据布局、运行时系统和垃圾收集器也是如此

SIPs并不仅仅用于封装应用程序扩展,它是用一种唯一的机制同时起到保护和扩展的作用,而不是传统的两种机制——进程和动态代码加载。这样做的结果就是,Singularity只需要一个错误恢复模型、一个通讯机制、一个安全策略,一个编程模型,而不是使用现在的操作系统中的多层的部分重复机制和策略。一个关键的基于Singularity的试验就是建立一个完全使用SIPs的系统,并且能够展现出新系统相对于传统系统的可靠性。

       Singularity核心几乎全部都是安全代码,该系统的其它部分也都是可验证的安全代码,这些部分是在SIPs中执行,它们包括了所有的设备驱动,系统进程和应用程序。虽然所有的不可信代码的安全性必须是可验证的,Singularity的部分内核和运行时系统,我们称之为可信任库,它们的安全是不可验证的。语言安全性保护可信任库不受不安全代码影响。

       SIPs的完整性依赖于语言安全性和系统级不可变性(system-wide invariant),所谓的系统级别可变性,是说一个进程不会把一个副本保留在另一个进程的对象空间中。

       很明显,保证代码安全是很必要的。在短期内,Singularity依靠代码级编译器验证和中间代码保证代码安全。在未来的Singularity中,类型化的汇编语言(Typed Assembly Language, TAL)允许Singularity验证已编译代码[参考3638]的安全性。TAL允许一个可执行程序提供类型安全的凭证,这个凭证会由一个安全语言的编译器自动生成。在一个可执行程序中,验证一个凭证的正确性和对于指令的可应用性是对于一个为几千行代码准备的验证器来说是一个简单的任务。这个端到端验证策略可以为Singularity的可信任库省去一个巨大而且复杂的编译器。验证器必须经过仔细设计、实现和检查,但这些任务是可执行的,因为它规模不大,且简单。

       内存独立不可变性可以防止跨对象空间的指针用于多个目的。首先,它通过隐藏具体的实现和防治挂起的指针进入终止进程,增强了一个进程的数据提取和失败隔离。第二,它通过允许进程拥有不同的运行时系统和垃圾收集器来进行非协调运行?,减少了实现时的约束性。第三,它通过使一个进程对于一个特定内存的拥有权更加明确,使资源统计和回收更加清楚。最后,由于它不需要修改多类型指针和地址空间,简化了内核接口的实现。

       这个框架的主要问题在于消息传递间的通讯困难,这是相对于直接共享数据的复杂性而言的。Singularity通过一个高效的消息系统解决这个问题,编程语言扩展精确地定义了通道的通讯及验证工具[19]

2.1 可扩展性

       软件创造者很少参与用户需要的完全功能实现。与试图用一个单个系统满足所有人的需求相比,他们更愿意用为一些大型系统提供一种调用外部代码的机制。例如,微软的Windows系统,支持超过100,000种第三方设备驱动程序,这种机制允许该系统几乎可以控制任何一种硬件设备。同样的,无数的浏览器插件和扩展增加了浏览器的可用接口和Web页组件。甚至一些开源项目,虽然理论上是可以修改的,也提供插件机制,因为扩展程序更容易开发、部署和组合,这是相对于一个新的软件版本而言的。

       扩展程序通常是被动态加载到主程序的地址空间中的。由于可以直接访问主程序的内部接口和数据结构,扩展程序能够提供许多功能的实现。尽管如此,由于实现的复杂性会带来很高的成本,并且扩展程序是导致软件实现困难、安全和后续兼容性问题的主要原因。虽然扩展程序的代码经常是不可信的、无法验证的、错误的甚至可以说是不怀好意的,它在没有任何硬接口(hard interface)、边界和区分的情况下把自己载入到主程序的地址空间中,这样导致的结果通常会让人感到不满。例如,Swift报告说,85%的经过诊断的windows系统崩溃是由错误的设备驱动程序造成的。并且,由于扩展程序缺少硬接口,它可以使用未公开的主程序实现代码,但这样会妨碍整个程序的维护,且必须使用扩展测试来防止不兼容。

       动态代码加载也同样带来了另外一个不是很明显的问题,可能会造成性能和正确性问题。可以在开放环境中调用代码的软件,很难假设它的系统状态、不变量或者有效过渡(valid transitions),这一点可以看看Java虚拟机(JVM)。一个中断、异常或者线程切换可以调用代码来加载一个新的文件,重写类和方法的内容,并且修改全局状态[47]。总的来说,唯一的可用方法是分析运行在特定条件下的程序,用普通的假设环境启动,即这个环境中不能强制在任何两个操作之间改变什么。

       一个替换方法是保护代码加载和在它自己的环境中动态隔离已创建的代码。用这种方法,之前的一些尝试通常不是很广泛地被使用,因为隔离机制通常有性能和编程方面的问题,这导致它甚至比不上没有隔离机制的运行。最常见的机制是一个传统的系统进程,但由于它的运行成本高,限制了它的可用性。内存管理硬件提供硬件边界和处理器状态保护,但它也会造成跨进程控制和数据传输的成本十分昂贵,在一个x86处理器上,进程间交换需要消耗几百到几千个运行周期不等,这还不包括TLB和缓存充填消耗的[参考25]

       更多的现行系统,例如Java虚拟机和微软的通用语言运行时(CLR),都是为可扩展性和使用语言安全而设计的,这些特性不是由硬件实现的,它是通过在同一个地址空间中隔离计算运行的机制实现的。安全语言,他们自己并不能保证隔离。共享数据可以在两个计算对象空间之间提供可导航的通道,通过这个通道,指针映射机制可以实现数据提取和数据隐藏。这样的结果是,这些系统必须有复杂的安全机制和策略,例如Java的细粒度访问控制(fine grain access control)或者CLR的代码安全访问,这些都是为了限制对系统机制和接口的访问而准备的。这些机制很难得到很好的使用,也很难实现自顶向下的控制。

       同样重要的是,共享运行时系统和在同一进程中执行的计算是不会在失败的时候被隔离的,当一个JVM计算运行失败时,整个JVM进程会重启,因为它很难隔离和抛弃已经被破坏的数据,也很难找到一个清晰点来重启失败的计算。

       Singularity使用SIPs来进行封装,每一个设备驱动器、系统进程、应用程序和扩展程序在自己的SIP中运行,并且在通道间通讯,这可以限制一些功能的实现。如果代码在SIP中运行失败,它会终止,同时,系统会收回资源并且通知与它通讯的其他程序。因为其他程序并不会与扩展程序共享状态,错误恢复是完全在本地进行的,是由基于通道的精确协议负责处理的。

       另一个新代码的运行时来源是动态代码生成,通常封装在接口映射中。这个特性允许一个运行程序检查现有代码和数据,生成并安装新的方法。反射经常用于为XML模式生成编组代码或解析器。Singularity的封闭SIPs不允许运行时代码生成。

       作为替换,Singularity提供了编译时反射(Compiled-Time Reflection)功能,它可以提供类似的在编译完成后执行的功能。普通反射可以访问运行时的值,这要比运行时反射更加强大。然而,在许多情况下,经过编组的类或者经过解析的模式是在执行之前就知道的。在这种情况下,CTR可以在编译过程中生成代码,其他情况下,Singularity支持在独立的SIP中生成和运行代码。

 

2.2     应用提取

操作系统现在通常不会把程序或应用程序作为第一类提取。一个现代的应用程序是代码、数据和原数据文件的集合,这些文件是用不被信任的代理安装的,是通过拷贝它们进入文件系统,并且在命名空间中注册它们实现的。操作系统对于这样的文件之间的关系知之甚少,对于整个安装过程没有任何控制可言。

Singularity中,应用程序都会有一份清单(manifest)和一个资源集合。这个清单通常用于描述应用程序的资源和它们之间的依赖性。虽然许多现存的安装描述既有声明又有安装规则,但Singularity的清单仅仅包含声明,这些声明中有应用程序在安装或升级完成后的状态。

实现这个状态是Singularity的职责所在。一份清单必须为Singularity安装程序提供足够的信息来产生满足需要的安装步骤、对现存程序的冲突检测,以及判断安装是否成功。Singularity可以防止安装程序影响系统性能。

Singularity的其它部分也利用来自清单的信息。例如,Singularity的安全模型会把应用程序作为一个安全准则(security principal),这个安全准则把应用程序加入到文件访问控制列表中(ACL)。要把一个应用程序变成规则,需要应用程序构成信息和依赖性信息,以及一个强身份(strong identity),所有这些信息都是从清单中获得的。

2.3     探讨

Singularity的一些关键贡献包括

l         构建了一个软件隔离进程的系统和应用程序模型,它会使用已验证的安全代码在进程间实现一个强边界,这个过程不存在硬件机制介入。因为创建和安排SIP的系统成本很低,系统和应用程序可以支持更多、更好的隔离边界和强隔离模型。

l         一个坚固的系统和应用程序扩展模型,简化了安全模型,增加了可靠性和失败恢复,增加了代码优化和把编程和测试工具变得更加有效。

l         一个更快、可验证的进程间通讯机制,它可以保证进程独立性和隔离性,并且它在保证正确通讯的情况下消耗很少的系统资源。

l         语言和编译器支持在安全代码中构建整个系统,使用简单快捷的资源管理实现验证进程间通讯

l         操作系统和安全语言运行时系统的区别将不复存在,这里的安全语言系统是指JVMMicrosoft CLR

l         普遍深入地在整个系统中使用规范描述、配置和验证组件。

 

这份报告的剩余部分是这样划分的。第三章介绍Singularity的详细系统实现,第四章探讨Singularity编程语言和编译器支持,第五章介绍Singularity的系统服务,第六章提供给你一些性能测试报告,第七章探讨相关作品,第八章总结全文。

3           Singularity 架构

1中,向我们展示了Singularity操作系统的架构,它是由三个主要对象组成的:内核、软件隔离进程和通道。内核为系统提供核心功能,包括内存管理、进程创建和终止、通道操作、任务安排和输入输出操作。和其他的微内核一样,这个系统中的大部分功能和扩展性存在于内核以外的进程中。

3.1 可信任库 (Trusted Base)

       Singularity中的代码要么是需要验证的,要么是可信任的。需要验证的代码类型和内存安全是由编译器负责检查的。不可验证的代码必须是可被系统信任的代码,并且受限于硬件抽象层(Hardware Abstraction Layer, HAL)、内核和运行时系统的一些部分。大多数内核是可验证的且是安全的,但有一部分不是,它们是在汇编程序、C++和不安全c# unsafe c#)中完成的。

       所有其他代码都是在安全语言中完成的,并且被翻译为安全的Microsoft Intermediate Language (MSIL),并最终通过Bartok编译器编译为x86汇编代码。目前,我们相信Bartok会正确验证并且生成安全代码。从长远来看,这很明显是不能让人满意的,我们计划使用类型化的汇编语言来验证编译器输出,并且将这部分的可信任计算库缩小为一个小型验证器。 [参考36]

       这两种代码的分界线由于运行时系统变得十分模糊。这种可信任的,但是不可验证的代码,计算和代码被有效地隔离开来,其中计算代码的已验证安全防止代码与运行时系统及它的数据结构进行交互操作,除非通过安全接口才可以。Singularity编译器能够内联一些例程(routine),因此可以安全地把一些原本在内核中进行的操作放入用户进程。

3.2     内核

Singularity内核是一个具有特权的系统组件,它控制着硬件资源的访问权限,分配和回收内存资源,创建和安排线程,提供内进程线程同步,并且管理输入输出操作。该内核是由安全的c#代码和不安全的c#代码混合而成的,运行在它的垃圾收集对象空间中。

除了通常的消息传递通道机制外,进程与内核进行通讯是通过一个强版本应用程序二进制接口(strongly versioned Application Binary Interface)完成的,该接口会调用内核代码中的静态方法,而且它遵循系统其他部分的设计风格,也是隔离内核和进程对象空间的。所有传递给ABI的参数都是值,而不是指针,所以核心和进程中的垃圾收集器不需要一致,唯一的不同是ABI函数的位置,我们的垃圾收集器目前不进行代码重定位,但如果他们这么做,那么这些方法就得维护在已知地址上的不变量。

ABI保持系统级的状态隔离不可变:一个进程不能用ABI改变另一个进程的状态。只有两种特殊情况,一个ABI调用仅对它调用的进程产生影响,这两种情况仅在子进程运行之前或之后改变它的状态,但不是在执行时。第一种调用是用于创建子进程的调用,该调用会在子进程执行之前加载子进程代码。第二种调用是终止子进程,这种调用会在线程终止执行后回收它使用过的资源。状态隔离确保一个Singularity进程仅有对自己状态的控制权。

3.2.1          句柄表

内核导出同步结构——互斥量,自动和手动重置事件——整理一个进程中的线程。一个线程通过一个强类型的、不透明(opaque)句柄操作这些构建器,这个句柄指向核心句柄表。强类型防止一个进程改变或者合并句柄。另外,句柄表中的插槽仅在一个进程终止之后才会被释放,这是用来防止进程释放互斥量的,保留它的句柄,并使用句柄操作另外一个进程对象的。尽管如此,Singularity确实在进程中重用表入口,因为这个原因,保留句柄会引起一个良性的,但很令人痛苦的错误,它会在进程中发生。

你可能感兴趣的:(project)