Operating System Conceptions第二章知识整理总结
小结:
这几天我看了《Operating System Conceptions》的第二章。
第二章先从用户、开发者以及计算机系统的角度开始,展示操作系统所提供的服务,继而讲解了操作系统是如何通过系统调用来为系统提供服务的,阐述一段程序是如何在系统中装入链接以及执行的。同时通过比较和对比整体、分层、微核、模块化和混合策略操作系统的不同设计,向我们展示了macOS、Android、Windows三种不同的操作系统并告诉我们如何去设计一个操作系统,并如何去调试它。最后作者对启动操作系统的过程进行了说明,描述了计算机是如何启动操作系统的。
读完第二章,让我对操作系统的结构有了一个更加明晰的理解,通俗地来想,操作系统就是在硬件上为程序的执行提供一个环境。由于硬件设施的不同以及我们所要求操作系统注重提供的服务不同等,关于操作系统的设计和组织因而因事制宜。在看到计算机如何启动时,想到以前的问题:我们想要计算机启动,那必须先运行程序,但是计算机不启动就无法运行程序,现在明白了这样一个问题的关键就在于BIOS了。本章中关于程序的装入和链接以及计算机如何启动的过程讲解比较笼统,不像我之前学的教材阐述得细致,应该在后文中会详细解说。但是在操作系统设计这一节的讲解中,我第一次有了一种以软件工程的角度,像看待软件设计那样,去看待一款操作系统的设计与实现,文中提出的机制和政策分离原则,机制决定如何做某事,而政策决定需要做的是什么,这和软工中的需求和实现是相像的,但不同的是软工中实现是紧密围绕着需求来的,OS中政策和机制是分离原则的,软工中的需求主要是以人为核心的,OS中政策是主要是围绕系统提供的服务的。一个因地制宜的操作系统是各种算法和策略实现的基础。
此外,我还了解到,Computer Science被分为四个大类:AI, Programming Language, Systems, Theory.其中Systems的内容十分广泛,包括OS, Architecture, Network等等,它最好的会议是OSDI和SOSP,这两个会议方向很广,影响很大,每两年开一次,两个会议轮流开。除此之外,在OS方向还有一些方向比较专一,但是仍然很高级别的会议,比如FAST就是File and storage system最好的会议,NSDI偏重Networked System Design and Implementation, RTSS是Real system最好的会议,而 ISCA,HPCA和MICRO是Architecture领域最好的会议,还有像ASPLOS则是OS, Architecture, Programming language三个领域交叉的最好会议。
整理与思考:
2.2 Operating-System Services
操作系统提供服务:
用户界面,程序执行,I/O操作,文件系统操作,通信,错误检测
用户与操作系统进行交互的三种基本方法:命令解释器,图形用户界面,触摸屏界面
2.3 System Calls
什么是系统调用?
所谓系统调用就是用户在程序中调用操作系统所提供的一些子功能,系统调用可以被看作特殊的公共子程序。系统中的各种共享资源都由操作系统统一掌管,因此在用户程序中,凡是与资源有关的操作(如存储分配、进行I/0传输以及管理文件等),都必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。通常,一个操作系统提供的系统调用命令有几十乃至上百条之多。
系统调用的存在的意义:
1)用户程序通过系统调用来使用硬件,而不用关心具体的硬件设备,这样大大简化了用户程序的开发。比如:用户程序通过write()系统调用就可以将数据写入文件,而不必关心文件是在磁盘上还是软盘上,或者其他存储上。
2)系统调用使得用户程序有更好的可移植性。只要操作系统提供的系统调用接口相同,用户程序就可在不用修改的情况下,从一个系统迁移到另一个操作系统。
3)系统调用使得内核能更好的管理用户程序,增强了系统的稳定性。因为系统调用是内核实现的,内核通过系统调用来控制开放什么功能及什么权限给用户程序。这样可以避免用户程序不正确的使用硬件设备,从而破坏了其他程序。
4)系统调用有效的分离了用户程序和内核的开发。用户程序只需关心系统调用API,通过这些API来开发自己的应用,不用关心API的具体实现。内核则只要关心系统调用API的实现,而不必管它们是被如何调用的。
要想实现系统调用过程,可以总结为以下几个方面:1. 通知内核调用一个哪个系统调用;2. 用户程序把系统调用的参数传递给内核;3. 用户程序获取内核返回的系统调用返回值
这样,操作系统的运行环境可以理解为:用户通过操作系统运行上层程序(如系统提供的命令解释程序或用户自编程序),而这个上层程序的运行依赖于操作系统的底层管理程序提供服务支持,当需要管理程序服务时,系统则通过硬件中断机制进入核心态,运行管理程序;也可能是程序运行出现异常情况,被动地需要管理程序的服务,这时就通过异常处理来进入核心态。当管理程序运行结束时,用户程序需要继续运行,则通过相应的保存的程序现场退出中断处理程序或异常处理程序,返回断点处继续执行。
系统调用可以大致分为六个主要类别:
1、进程控制。完成进程的创建、撤销、阻塞及唤醒等功能。
2、文件管理。完成文件的读、写、创建及删除等功能。
3、设备管理。完成设备的请求或释放,以及设备启动等功能。
4、信息维护。保存有关其所有进程的信息,并使用系统调用来访问此信息。
5、通信。完成进程之间的消息传递或信号传递等功能。
6、保护。提供了一种机制来控制对计算机系统所支持的资源的访问。
为什么应用程序编程人员更喜欢根据API而不是调用实际的系统调用来编程呢?
这样做有几个原因。一个好处是程序可移植性。一个应用程序编程器使用一个API设计一个程序可以期望她的程序能够编译和运行。在任何支持相同API的系统上运行(尽管在现实中,架构上的差异常常使这比它可能出现的困难更大)。此外,实际的系统调用通常比应用程序编程人员可用的API更详细、更难以使用。不过尽管如此,在内核中,API中的函数与其相关联的系统调用之间常常存在很强的相关性。
2.4 System Service
系统提供以下几种服务:
- 文件管理。
- 状态信息管理。
- 文件修改。
- 编程语言的支持。
- 程序装入和执行。
- 通信。
- 后台服务。
除了系统程序之外,大多数操作系统还提供了一些程序,比如web浏览器、文本格式器、电子表格、数据库系统、编译器、绘图和游戏等。
2.5 Linkers and Loaders
为什么有些系统会在存储在固件中,而另一些系统将其存储在磁盘上?
对于某些设备,如PAD,可能无法为设备提供一个带有文件系统的磁盘。在这种情况下,操作系统必须存储在固件中。
程序装载链接的过程:
1、将源文件被编译成目标文件,这些文件被设计成可以加载到任何物理位置,这种格式被称为可重新定位的对象。
2、链接器将这些可重新定位的文件合并到一个二进制可执行文件中,而加载器将二进制可执行文件加载到内存中,使之在内存中有资格在CPU内核上运行。
3、与链接和加载相关联的活动是重新定位,它将最终地址分配给程序部分,并调整程序中的代码和数据,以匹配这些地址。
2.6 Why Applications Are Operating-System Specific
每个操作系统都提供了一组独特的系统调用。系统调用是由操作系统提供的服务集的一部分,供应用程序使用。即使系统调用在某种程度上是一致的。其他的障碍将使我们很难在不同的操作系统上执行应用程序。
一个应用程序可以在多个操作系统上运行的三种方式:
1、应用程序可以用一种在多个操作系统上拥有解释器的语言编写。解释器读取源程序的每一行,执行相应的指令,并调用本机操作系统调用。
2、应用程序可以用一种包含运行应用程序的虚拟机来编写。
3、开发人员可以使用标准语言或API,编译器在机器和操作系统特定语言中生成二进制文件,应用程序必须被移植到它将要运行的每个操作系统上。
2. 8 Operating-System Structure
操作系统的组件是如何相互连接并合并成内核的。
传统的UNIX系统结构:
1)单片结构
将内核的所有功能放入一个单一的、静态的二进制文件中,该文件在单个地址空间中运行,这种方法被称为单片结构,是设计操作系统的常用技术。
应用:
这种有限结构的一个例子是原始的UNIX操作系统,它由两个可分离的部分组成:内核和系统程序。内核进一步分离成一系列的接口和设备驱动程序,系统调用接口和物理硬件之上的所有东西都是内核。内核通过系统调用提供文件系统、CPU调度、内存管理和其他操作系统功能。总而言之,这是一个巨大的功能,可以将其组合成一个单一的地址空间。
缺点:尽管单片内核显然是简单的,但它们很难实现和扩展。
优势:系统调用接口的开销非常小,内核内部的通信速度很快。
2)分层方法
单片方法通常被称为紧密耦合系统。
设计一个低耦合的系统使之在许多方面进行模块化。一种方法是分层的方法,在这种方法中,操作系统被分解成若干层(级别)。底层(第0层)是硬件,最高的(层N)是用户界面。
分层方法对系统设计的主要优点是什么?
该系统更容易调试和修改,因为更改后只影响系统的部分,而不触及操作系统的所有部分。
使用分层方法的缺点是什么?
信息只保存在需要的地方,并且只能在一个受限制的区域内被访问,因此任何影响该数据的bug都必须被限制在一个特定的模块或层中。
这种模块化方法的优点是,一个组件的更改只影响该组件,而不影响其他组件,从而允许系统实现者在创建和更改系统的内部工作时获得更多的自由。
操作系统层是由数据组成的抽象对象的实现,以及能够操作这些数据的操作。一个典型的操作系统层,层m由数据结构和一组函数组成,这些函数可以被更高层次的层调用。反过来,层m可以调用底层上的操作。每一层都只在底层提供的操作中实现。上层不需要知道这些操作是如何实现的,它只需要知道这些操作的作用。因此,每一层都隐藏了来自高层层的某些数据结构、操作和硬件的存在。
3)微内核
微内核是通过从内核中删除所有非必需组件并将其作为驻留在单独地址空间中的用户级别程序来构造操作系统。
微内核的作用是什么?
在客户端程序和在用户空间中运行的各种服务之间提供通信。通信是通过消息传递提供的。例如,如果客户端程序希望访问一个文件,那么它必须与文件服务器交互。客户端程序和服务永远不会直接交互,它们通过与微核交换消息来间接地进行通信。
微内核方法的优势是它使扩展操作系统变得更容易。所有的新服务都被添加到用户空间中,并且不需要修改内核。当内核必须被修改时,更改是很少的。由此产生的操作系统更容易从一个硬件设计移植到另一个硬件设计。微核还提供了更多的安全性和可靠性,因为大多数服务都是作为用户程序而不是内核来运行的。如果服务失败,操作系统的其余部分将保持不变。
4)可加载内核模块
可加载内核模块(LKMs):内核有一组核心组件,可以通过模块在启动时或运行时通过模块链接到其他服务。
设计的思想是内核提供核心服务,而其他服务是动态实现的,动态链接服务比直接向内核添加新功能更可取,这需要在每次做出更改时重新编译内核。
与微内核方法比较:相同:主要模块只有核心功能和如何加载其他模块和与其他模块通信;不同:可加载内核模块更高效,因为模块不需要通过消息传递来进行通信。
5)混合系统
许多现代操作系统都是使用单一的内核和模块组合而成的混合系统。
1 、macOS and iOS
这两个系统的架构层次包括以下内容:
1、用户体验层。
2、应用框架层。
3、核心框架。
4、内核环境。
此外,应用程序可以被设计为利用用户体验特性,或者绕过它们,直接与应用程序框架或核心框架交互。此外,应用程序可以完全放弃框架,直接与内核进行通信。
macos和ios之间的一些重要区别包括以下内容
- macos是为台式机和笔记本电脑系统设计的,所以被编译成在英特尔的架构上运行。ios是为移动设备设计的,因此被编译为基于arm的架构。
- ios的安全设置比macos更严格。与macos相比,ios操作系统对开发者的限制更大,甚至可能对开发者关闭。
2、安卓
Android与IOS类似,因为它为一系列软件提供了丰富的框架,支持图形、音频和硬件功能。这些特性反过来又为开发在众多android设备上运行的移动应用程序提供了一个平台。
Google为Java开发设计了一个独立的Android API。Java应用程序被编译成可以在Android运行时ART上执行的表单,这是一个为Android设计的虚拟机,针对具有有限内存和CPU处理能力的移动设备进行了优化。Java程序首先被编译成Java字节码、类文件,然后翻译成可执行文件。应用程序当安装在设备上时,.dex文件被编译成原生机器码,从而在ART上执行。AOT编译允许更高效的应用程序执行,以及减少功耗,这对于移动系统来说是至关重要的。
开发人员可以绕过虚拟机使用Java原生接口或JNI来编写能够访问特定硬件特性的Java程序。
谷歌通过软硬件抽象层或HAL来抽象物理硬件,因此Android可以在几乎无限的硬件设备上运行。
2.9 Building and Booting an Operating System
1)操作系统生成
配置系统涉及到指定将包含哪些特性,这取决于操作系统。通常,描述系统如何配置的参数被存储在某种类型的配置文件中,并且一旦创建了这个文件,它可以用多种方式使用:
1、系统管理员可以使用它来修改操作系统源代码的副本,然后操作系统被完全编译(称为系统构建)。数据声明、初始化和常量,以及编译,生成了操作系统的输出对象版本,该操作系统是根据配置文件中描述的系统定制的。此外,系统配置可以从现有的库中选择预编译的对象模块。这些模块被链接在一起,形成生成的操作系统。
2、构建一个完全模块化的系统,此时,选择预编译的对象模块发生在执行时,而不是在编译或链接时间。
这些方法的主要区别是生成的系统的大小和通用性,以及随着硬件定量的变化而对其进行修改的方便性。对于嵌入式系统,采用第一种方法并为特定的、静态的硬件配置创建操作系统并不少见。然而,大多数支持台式机和笔记本电脑以及移动设备的现代操作系统采用了第二种方法。
2)系统启动
系统启动引导过程:
1、由引导程序或引导加载程序的代码定位内核。
2、内核被加载到内存中并开始执行。
3、内核初始化硬件。
4、安装根文件系统。
如何选择启动一个操作系统?引导程序需要做什么?
考虑一个系统,它想同时运行Windows XP和三种不同的Linux发行版,每个操作系统将存储在磁盘上。在系统启动时,一个特殊的程序Boot Manager将决定要启动哪个操作系统。这意味着引导管理器将在系统启动时运行。引导管理器负责要引导哪个系统。引导管理器通常为用户提供一系列的引导系统,如果用户没有选择任何选择,那么引导管理器通常引导到默认的操作系统中。
(扩:)计算机一个完整的启动过程可以解释如下:
第一阶段:BIOS
BIOS:基本输出输入系统Basic Input/Output System
1) 硬件自检
BIOS程序首先检查,计算机硬件能否满足运行的基本条件,这叫做"硬件自检"(Power-On Self-Test),缩写为POST。
如果硬件出现问题,主板会发出不同含义的蜂鸣,启动中止。如果没有问题,屏幕就会显示出CPU、内存、硬盘等信息。
2) 启动顺序
硬件自检完成后,BIOS把控制权转交给下一阶段的启动程序。
这时,BIOS需要知道,"下一阶段的启动程序"具体存放在哪一个设备。也就是说,BIOS需要有一个外部储存设备的排序,排在前面的设备就是优先转交控制权的设备。这种排序叫做"启动顺序"(Boot Sequence)。
第二阶段:主引导记录
BIOS按照"启动顺序",把控制权转交给排在第一位的储存设备。
这时,计算机读取该设备的第一个扇区,也就是读取最前面的512个字节。如果这512个字节的最后两个字节是0x55和0xAA,表明这个设备可以用于启动;如果不是,表明设备不能用于启动,控制权于是被转交给"启动顺序"中的下一个设备。
这最前面的512个字节,就叫做"主引导记录"(Master boot record,缩写为MBR)。
1) 主引导记录的结构
"主引导记录"只有512个字节,放不了太多东西。它的主要作用是,告诉计算机到硬盘的哪一个位置去找操作系统。
主引导记录由三个部分组成:
(1) 第1-446字节:调用操作系统的机器码。
(2) 第447-510字节:分区表(Partition table)。
(3) 第511-512字节:主引导记录签名(0x55和0xAA)。
其中,第二部分"分区表"的作用,是将硬盘分成若干个区。
2) 分区表
硬盘分区有很多好处。考虑到每个区可以安装不同的操作系统,"主引导记录"因此必须知道将控制权转交给哪个区。
分区表的长度只有64个字节,里面又分成四项,每项16个字节。所以,一个硬盘最多只能分四个一级分区,又叫做"主分区"。
每个主分区的16个字节,由6个部分组成:
(1) 第1个字节:如果为0x80,就表示该主分区是**分区,控制权要转交给这个分区。四个主分区里面只能有一个是**的。
(2) 第2-4个字节:主分区第一个扇区的物理位置(柱面、磁头、扇区号等等)。
(3) 第5个字节:主分区类型。
(4) 第6-8个字节:主分区最后一个扇区的物理位置。
(5) 第9-12字节:该主分区第一个扇区的逻辑地址。
(6) 第13-16字节:主分区的扇区总数。
最后的四个字节("主分区的扇区总数"),决定了这个主分区的长度。也就是说,一个主分区的扇区总数最多不超过2的32次方。
如果每个扇区为512个字节,就意味着单个分区最大不超过2TB。再考虑到扇区的逻辑地址也是32位,所以单个硬盘可利用的空间最大也不超过2TB。如果想使用更大的硬盘,只有2个方法:一是提高每个扇区的字节数,二是增加扇区总数。
第三阶段:硬盘启动
这时,计算机的控制权就要转交给硬盘的某个分区了,这里又分成三种情况。
情况A:卷引导记录
上一节提到,四个主分区里面,只有一个是**的。计算机会读取**分区的第一个扇区,叫做"卷引导记录"(Volume boot record,缩写为VBR)。
"卷引导记录"的主要作用是,告诉计算机,操作系统在这个分区里的位置。然后,计算机就会加载操作系统了。
情况B:扩展分区和逻辑分区
随着硬盘越来越大,四个主分区已经不够了,需要更多的分区。但是,分区表只有四项,因此规定有且仅有一个区可以被定义成"扩展分区"(Extended partition)。
所谓"扩展分区",就是指这个区里面又分成多个区。这种分区里面的分区,就叫做"逻辑分区"(logical partition)。
计算机先读取扩展分区的第一个扇区,叫做"扩展引导记录"(Extended boot record,缩写为EBR)。它里面也包含一张64字节的分区表,但是最多只有两项(也就是两个逻辑分区)。
计算机接着读取第二个逻辑分区的第一个扇区,再从里面的分区表中找到第三个逻辑分区的位置,以此类推,直到某个逻辑分区的分区表只包含它自身为止(即只有一个分区项)。因此,扩展分区可以包含无数个逻辑分区。
但是,似乎很少通过这种方式启动操作系统。如果操作系统确实安装在扩展分区,一般采用下一种方式启动。
情况C:启动管理器
在这种情况下,计算机读取"主引导记录"前面446字节的机器码之后,不再把控制权转交给某一个分区,而是运行事先安装的"启动管理器"(boot loader),由用户选择启动哪一个操作系统。
第四阶段:操作系统
控制权转交给操作系统后,操作系统的内核首先被载入内存。
以Linux系统为例,先载入/boot目录下面的kernel。内核加载成功后,第一个运行的程序是/sbin/init。它根据配置文件(Debian系统是/etc/initab)产生init进程。这是Linux启动后的第一个进程,pid进程编号为1,其他进程都是它的后代。
然后,init线程加载系统的各个模块,比如窗口程序和网络程序,直至执行/bin/login程序,跳出登录界面,等待用户输入用户名和密码。
至此,全部启动过程完成。