1操作系统简介
当今世界上大多数人对Windows、Linux、MacOS等操作系统有使用经验,但对操作系统本身的认识往往不多。
毕竟与用户直接打交道的大多数是各种炫酷的客户端软件,包含精美的字体、图标和图片等。虽然一般来说这样漂亮的用户界面通常不属于操作系统,但可看作它是操作系统的一部分,通过操作系统来完成其底层复杂的与硬件层面的交互工作。
现今的计算机是一个极其复杂的系统,一般由一个或多个处理器,以及一些主内存、磁盘、网络接口、各种外设以及各种其他输入/输出设备组成。如果每个程序员都必须详细了解所有这些硬件层面的原理和机制,然后才能编写相应的代码,那么可想而知这样的效率会有多低。
此外,管理所有这些组件并优化使用它们是一件非常困难的工作。因此,计算机配备了一层称为操作系统的软件,其作用是为用户程序提供简洁方便的计算机模型,并统一管理所需要的资源。
在这里,我们看到计算机最底部是各种硬件,包括芯片,主板,磁盘,键盘,显示器等。在硬件之上则是软件。大多数计算机都有两种操作模式:内核模式和用户模式。
内核模式(Kernel Mode):在内核模式下,程序具有硬件的所有控制权限,可以执行所有CPU指令,可以访问任意地址的内存。在内核模式下的任何异常都是灾难性的,将会导致整台机器停机。
用户模式(User Mode):在用户模式下,程序没有对硬件的直接控制权限,也不能直接访问地址的内存。程序是通过调用系统接口(API)来访问硬件和内存。在这种保护模式下,即使程序发送崩溃也是可以恢复的。
操作系统可以看作是最基本的软件,它运行在内核模式(也称为管理程序模式)下。在这种模式下,它可以完全访问所有硬件,并且可以执行机器可以执行的任何指令。
而在用户模式下运行的一般都为直接面向用户的应用软件,在该模式下只能使用一部分机器指令。特别是,那些影响机器控制或「I/O输入输出」的指令在用户模式程序中是禁止的。
2 操作系统作用
看起来操作系统好像就是横跨在用户应用软件和底层硬件之间的桥梁,通过运行在内核模式进行硬件的控制和调度。事实上,操作系统的作用可大致分为两大方面。
作为计算机硬件的抽象层
大多数计算机在机器语言级别上的体系结构是极其原始的,主要说的是指令集,内存组织,I/O处理和总线结构。在这样的结构下是没办法进行自定义编程的,特别是对于I/O操作而言。因而,没有理智的程序员愿意在硬件级别处理磁盘和内存。
为了解决这样尴尬的局面,从而产生了一个称为磁盘驱动程序的软件。它用来处理硬件并提供一个接口来读取和写入磁盘块,而使得我们无需关注其细节。这本身就是操作系统最初具有的功能之一,包含许多用于控制I/O设备的驱动程序。
但是对于大多数应用程序来说,光有驱动程序是不够的。因此,所有操作系统都为使用磁盘提供了另一层抽象而又便于理解的概念:文件。程序可以创建,写入和读取文件,而不需要处理硬件层面中复杂的工作原理和实现细节。
这种所谓对硬件层面的抽象是管理所有这些复杂底层硬件的关键。一个好的抽象系统将几乎不可能完成的任务变成可以进行管理的任务。所以事实上操作系统所干的事,首先是定义和实现抽象。然而是使用这些抽象来解决上层用户的问题。
「文件」是几乎每个计算机用户都可以理解的一种抽象。一个文本,一个照片,一封保存的电子邮件,一首周杰伦的歌或一个网页,这些都可看作是抽象出来的文件。正是通过操作系统的抽象作用,使得我们可以在处理文本、照片、邮件的时候只需要点击几下,而不需要去考虑在SATA磁盘中数据的存放格式发生了如何变化。
显而易见的是,操作系统的主要任务之一是隐藏硬件,并以简单且好看的抽象形式呈现程序,以方便用户的使用。这也是Windows系统持续几十年的市场占额的原因所在,相较于其它的系统,Windows以更接近用户体验的抽象方式从而赢得了市场。
作为计算机的资源管理器
上面的作用简单来说就是操作系统为用户层面的应用程序提供了一种硬件的抽象,是自顶向下的作用。除此之外,操作系统还可以作为自底向上的中间人对应用程序所需要的资源进行管理和分发。在自下而上的流程中,操作系统的工作是在需要处理器、内存和I/O设备的各种程序之间提供有序且受控的分配。
打个比方,我们知道操作系统允许多个程序同时运行并占用内存。 想象一下,如果在一台计算机上运行的三个程序都试图在同一台打印机上同时打印输出,将会发生什么。 三个程序调用打印机打印可看作是对资源对请求,如果没有操作系统对资源请求的管理和分配,那么整个计算机的输出结果将会是完全混乱的。
打印机这个例子还只是最简单直接的资源请求和分配问题,而其它比如网络资源、内存资源的请求冲突问题的影响就更大了。如果处理不好资源请求之间的问题,不同应用程序之间则会发生严重的串扰,以致出现各种无法调试的错误。
操作系统主要是以时间和空间这两种方式进行复用或共享资源。对资源进行时分复用时,不同的程序或用户轮流使用它。他们中的第一个开始使用资源,然后再使用另一个,依此类推。
这实际上就是所谓时间片的概念,在时间的纬度上为每个程序分配一段资源占用的机会。还是共享打印机的例子,当有多个程序都想用打印机的时候,就按照时间发送的顺序在打印机上线性排序,一个一个来。
另一种是空间多路复用。最主要的区别在于不是每个程序轮流使用,而是每个程序都获得了一部分资源。
例如,通常将主存储器划分为几个可同时使用的区域,每个程序可以同时占用。假设有足够的内存来容纳多个程序,那么相较于所有内存分配个一个程序而言,一次将多个子内存分配给多个程序就更有效。
当然,这会引起公平性以及安全性等问题,这也是操作系统需要解决的问题之一。进行空间多路复用的另一个资源是磁盘。在绝大数系统中,单个磁盘可以同时容纳来自多个程序或用户的文件。而操作系统在这其中的作用就是根据用户或程序来确定其对应的一块磁盘区域,从而保证程序正常的运行。
3 操作系统架构
现在,我们已经了解了操作系统是干什么的,现在该看看了解下操作系统到底是怎么搭建起来的,这就是操作系统的架构设计。我们将在这里讨论单片系统,分层系统,微内核以及客户端服务器系统。
单片系统
显而易见,最常见的操作系统架构是单片(Monolithic Systems)结构,即整个操作系统以内核模式作为单个程序运行。操作系统是作为各种底层处理过程的集合,链接在一起成为一个大型可执行的二进制程序。
在这种架构的系统中,每个过程都可以自由调用任何其他过程,只要后者提供前者所需的一些有用的计算即可。能够调用所需的任何过程都是非常有效的,但是拥有成千上万个可以不受限制地相互调用的过程也可能导致系统笨拙且难以理解。同样,任何这些过程的崩溃都将使整个操作系统崩溃。
要在使用这种方法时构造操作系统的实际目标程序,首先要编译所有单个过程(或包含这些过程的文件),然后使用系统链接器将它们全部绑定到一个可执行文件中。就信息隐藏而言,基本上没有任何东西-每个过程对于其他过程都是可见的(与包含模块或包的结构相反,在结构中,模块或包中的大部分信息都被隐藏在模块内部,而只有正式指定的入口点可以从模块外部调用)。
但是,即使在单片系统中,也可能具有某些结构。通过将参数放在定义良好的位置(例如,在堆栈上),然后执行陷阱指令,来请求操作系统提供的服务(系统调用)。该指令将机器从用户模式切换到内核模式
并将控制权转移到操作系统,如图1-17中的步骤6所示。然后,操作系统获取参数并确定要执行哪个系统调用。之后,它索引到一个表,该表在插槽k中包含一个指向执行系统调用k的过程的指针(图1-17中的步骤7)。
该组织建议了操作系统的基本结构:
1.调用请求的服务过程的主程序。
2.一组执行系统调用的服务过程。
3.一组有助于服务程序的实用程序。
在此模型中,对于每个系统调用,都有一个服务过程负责处理并执行它。实用程序执行一些服务程序所需的操作,例如从用户程序中获取数据。该过程分为三层,如图1-24所示。
除了引导计算机时加载的核心操作系统之外,许多操作系统还支持可加载的扩展,例如I / O设备驱动程序和文件系统。这些组件按需加载。在UNIX中,它们称为共享库。在Windows中,它们称为DLL(动态链接库)。它们具有文件扩展名.dll和C:\ Windows \ system32目录
分层系统
图1-24的方法的一般化方法是将操作系统组织为层次结构,每个层次都基于其下一层构建。 以这种方式构造的第一个系统是由E. W. Dijkstra(1968)和他的学生在荷兰埃因霍温理工学院建造的THE系统。
THE系统是用于荷兰计算机Electrologica X8的简单批处理系统,它具有32K的27位字(当时的价格昂贵)。
系统分为六层,如图1-25所示。 第0层处理处理器的分配,在发生中断或计时器到期时在进程之间切换。 在第0层之上,系统由顺序进程组成,可以对每个进程进行编程,而不必担心多个进程在单个处理器上运行这一事实。 换句话说,第0层提供了CPU的基本多重编程。
第1层完成了内存管理。它在主存储器中和512K字鼓上为进程分配了空间,该512K字鼓用于保存主存储器中没有空间的部分进程(页面)。在第1层之上,过程不必担心它们是在内存中还是在鼓上。第1层软件负责确保在需要页面时将页面带入内存,并在不需要页面时将其删除。
第2层处理每个流程与操作员控制台(即用户)之间的通信。在这一层的顶层,每个流程实际上都有自己的操作员控制台。第3层负责管理I / O设备并缓冲往返于它们之间的信息流。在第3层之上,每个进程都可以处理具有良好属性的抽象I / O设备,而不是具有许多特殊功能的实际设备。在第4层找到用户程序。他们不必担心进程,内存,控制台或I / O管理。系统操作员进程位于第5层。
MULTICS系统中对分层概念进行了进一步的概括。 MULTICS被描述为具有一系列同心环,而不是层,而内环比外环更具特权(实际上是同一件事)。当外环中的过程要调用内环中的过程时,它必须等效于系统调用,即
TRAP指令,其参数在允许进行调用之前已仔细检查其有效性。尽管整个操作系统是MULTICS中每个用户进程的地址空间的一部分,但是硬件使指定单个过程(实际上是内存段)成为可能,
阅读,写作或执行。
尽管THE分层方案实际上只是一个设计辅助,但由于系统的所有部分最终都链接在一起成为一个可执行程序,因此在MULTICS中,环形机制在运行时非常存在,并由硬件实施。环形机制的优点是可以轻松地扩展为结构用户子系统。例如,一位教授可以写一个
程序对学生程序进行测试和评分,并在n环中运行该程序,而学生程序在n + 1环中运行,以使他们无法更改其成绩。
微内核
通过分层方法,设计人员可以选择在哪里绘制内核用户边界。传统上,所有层都进入内核,但这不是必需的。实际上,可以采取尽可能减少内核模式的做法,因为内核中的错误会立即导致系统崩溃。相反,可以将用户进程设置为具有较小的功能,以使错误不会致命。
许多研究人员反复研究了每1000行代码中的错误数量(例如Basilli和Perricone,1984; Ostrand和Weyuker,2002)。错误密度取决于模块大小,模块寿命等,但是对于严重的工业系统而言,每千行代码中有两个到十个错误。
这意味着有五百万行代码的单片操作系统可能包含10,000至50,000个内核错误。当然,并非所有这些都是致命的,因为某些错误可能是在很少发生的情况下发出不正确的错误消息之类的事情。不过,操作系统的漏洞十足,计算机制造商会在其上放置重置按钮(通常在前面板上),
尽管这些设备中包含大量软件,但电视机,音响和汽车制造商却没有这样做。
微内核设计的基本思想是通过将操作系统划分为定义明确的小型模块来实现高可靠性,其中只有一个(微内核)以内核模式运行,其余模块以相对无能为力的普通用户进程运行。特别是,通过将每个设备驱动程序和文件系统作为单独的用户进程运行,其中一个错误可能会使该组件崩溃,但不能使整个系统崩溃。因此,音频驱动程序中的错误将导致声音混乱或停止,但不会使计算机崩溃。相反,在内核中具有所有驱动程序的单片系统中,有故障的音频驱动程序可以轻松地引用无效的内存地址,并使系统立即停止运行。几十年来,已经实现并部署了许多微内核(Haertig等,1997; Heiser等,2006; Herder等,2006; Hildebrand,1992; Kirsch等,2005; Liedtke,1993,1995,1996; Herde等,2006)。 (Pike等,1992;和Zuberi等,1999)。除了基于Mach微内核的OS X(Accetta等,1986)以外,普通的台式机操作系统都不使用微内核。然而,
它们在任务关键且对可靠性有很高要求的实时,工业,航空电子和军事应用中占主导地位。一些较知名的微内核包括Integrity,K42,L4,PikeOS,QNX,Symbian和MINIX 3。操作系统最多
独立的用户模式进程。 MINIX 3是符合POSIX的开源系统,可从www.minix3.org免费获得(Giuffrida等,2012; Giuffrida等,
2013; Herder等人,2006年; Herder等,2009;和Hruby等人,2013)。
MINIX 3微内核只有大约12,000行C语言和大约1400行汇编程序,用于非常低级的功能,例如捕获中断和切换过程。 C代码管理和调度进程,处理进程间通信(通过在进程之间传递消息),并提供一组有关
40个内核调用,以允许操作系统的其余部分完成其工作。这些调用执行的功能包括将处理程序挂接到中断,在地址空间之间移动数据以及为新进程安装内存映射。 MINIX 3的过程结构如图1-26所示,其内核调用处理程序标记为Sys。时钟的设备驱动程序也位于内核中,因为调度程序进行交互
与之紧密联系。其他设备驱动程序作为单独的用户进程运行。在内核之外,系统被构造为三层进程,所有进程均以用户模式运行。最低层包含设备驱动程序。由于它们在用户模式下运行,因此它们对I / O端口空间没有物理访问权限,因此无法直接发出I / O命令。相反,要对I / O设备进行编程,驱动程序将构建一个结构,告诉要向哪个I / O端口写入哪些值,并进行内核调用告知
内核来做写。这种方法意味着内核可以检查以查看驱动程序正在从其被授权使用的I / O写入(或读取)。因此(与单片设计不同),有缺陷的音频驱动程序不会意外地在磁盘上写入。
在驱动程序之上的是另一个用户模式层,其中包含服务器,这些服务器完成操作系统的大部分工作。一台或多台文件服务器管理文件系统,进程管理器创建,销毁和管理进程,等等。用户程序通过向服务器发送短消息以请求POSIX系统调用来获得操作系统服务。例如,一个过程需要
进行读取会将消息发送到其中一个文件服务器,告诉它要读取的内容。一个有趣的服务器是转世服务器,其工作是检查其他服务器和驱动程序是否正常运行。如果检测到故障,将自动更换它,而无需任何用户干预。这样,系统可以自我修复,并可以实现高可靠性。
该系统有很多限制,限制了每个过程的功能。如前所述,驱动程序只能触摸授权的I / O端口,但是对内核调用的访问也基于每个进程进行控制,也可以将消息发送到其他进程。进程还可以授予其他进程有限的权限,以使内核访问其地址空间。例如,文件系统可以授予磁盘驱动器权限,以使内核将新读入的磁盘块放在文件系统地址空间内的特定地址处。所有这些限制的总和是,每个驱动程序和服务器都完全有能力执行其工作,仅此而已,从而极大地限制了有故障的组件可能造成的损害。
在某种程度上与拥有最小内核有关的想法是将用于执行某些操作的机制置于内核中,而不是在策略中。为了使这一点更好,请考虑对流程进行调度。相对简单的调度算法是为每个进程分配数字优先级,然后让内核运行可运行的最高优先级进程。在内核中的机制是寻找最高优先级的进程并运行它。该策略(为流程分配优先级)可以由用户模式流程来完成。这样,可以将策略和机制分离,并使内核更小。
服务器客户端
微内核思想的一个细微变化是区分了两类进程,分别是服务器(每个服务器提供一些服务)和客户端(使用这些服务)。该模型称为客户端服务器模型。通常,最低层是微内核,但这不是必需的。本质是客户端进程和服务器进程的存在。
客户端与服务器之间的通信通常是通过消息传递。为了获得服务,客户端进程构造一条消息,说出它想要什么并将其发送到适当的服务。然后该服务执行工作并发送回答案。如果客户端和服务器恰好在同一台机器上运行,则可以进行某些优化,但是从概念上讲,我们仍然在这里讨论消息传递。
这种想法的一个明显的概括是让客户端和服务器运行在通过局域网或广域网连接的不同计算机上,如图1-27所示。由于客户端通过发送消息与服务器进行通信,因此客户端不需要知道消息是在其自己的计算机上本地处理还是通过网络发送到远程计算机上的服务器。
就客户端而言,在两种情况下都会发生相同的事情:发送请求并返回答复。因此,客户端-服务器模型是一种抽象,可以用于单台计算机或计算机网络。
越来越多的系统要求用户将其家用PC上的用户作为客户端,而将大型机作为服务器运行。 实际上,许多Web都是以这种方式运行的。 PC将对网页的请求发送到服务器,然后该网页返回。 这是网络中客户端-服务器模型的典型用法。