一、复杂性与系统抽象
1.系统与复杂性
1.1系统共性与系统复杂性
系统的共性问题
-
涌现性
surprise!
系统所表现出来的,通过各个组件难以观察或预测出的属性
-
影响传播
小系统没有大修改
因为系统是由不同的组件构成,这些组件产生的相互影响会沿着系统间的接口传播,可能导致意想不到的后果。
-
不成比例规模效应
当系统规模(或速度)增大时,某些部件有不同法则,从而可能导致系统失败
-
权衡取舍
系统中存在冲突的目标和因素,在设计中获得一方面的收益往往伴随着另一面
的损失,必须考虑权衡和取舍。
系统复杂性
复杂性的标志
组件多、交互多、不规则、描述长、人手多
1.2应对系统复杂性的方法
-
模块化 : 就是将系统分为互相交互的子系统,即模块(module),从而使当对系统进行分析和设计时,只需要分别考虑各个模块内交互,及模块间的交互
-
抽象 : 抽象的含义就是就是将规格定义与实现进行分离,让接口与内部相互独立,隐藏细节,使得外界只能通过接口来对其进行观察和操作
模块化几乎总是伴随着抽象,二者在一起时被称为“功能模块化”
例子
结构化编程语言:用函数将代码组成模块,模块只能通过调用来进行访问,函数的名称和形参就是对函数模块的抽象
面向对象的编程语言:将数据类型组成模块,并且为它们定义操作接口,这就是类。类的名与public 的方法与成员变量就是对类的抽象。
模版:模版是对函数和类进行的进一步的抽象。
模块化是设计的基础,抽象是设计中最重要的思想。
-
分层 : 分层是指对某个系统的功能模块化设计是由多个层层包含的模块组成的,每个模块都具备了系统完备的功能,但是每一个外层的模块都基于下层完备的功能,设计了更易于使用和更高层抽象的功能。
分层是模块化的一种特殊应用场景。可以认为是不断封装的模块化结构
例子
计算机系统之上的操作系统与应用系统、开发软件所需要的程序开发语言、计算机之间为进行数据传输而建立的网络通信协议栈,都体现了分层设计的思想。
分层的设计案例很多,例如:数据类型的实现:从整数、分数、复数到多项式的逐层实现;网络中点到点通信、端到端通信、语义交互的逐层实现
-
层级:层级是模块化的又一种特殊的组织方式,是多层嵌套的模块化
在社会管理中的分而治之,在实际的复杂工程系统的设计中所形成的系统结构,在生物体的进化中所形成的生物结构,通常都是以层级方式组织的。
分层和层级的区别
分层的每一层都能形成完备的功能,而层级只有模块整合之后才能形成完备的功能。
命名
命名不是应对系统复杂性的通用方法,但它却是抽象方法所不可缺少的基础。
命名使得人类思维的抽象成为可能,命名也使得系统设计的抽象成为可能。
命名是间接和共享的前提,是抽象和模块化的基石。
间接
当模块有了名字,其他模块就可以用这个名字马上访问它、未来访问它(称之为绑定)、
将访问它的能力传递给其他模块(共享)。而这种通过名字来访问的能力,就是间接。
间接解耦了模块,带来了灵活性、可扩展性。
1.3计算机系统的特殊性与应对
计算机系统的特殊性
-
计算机系统的扩张没有上限
-
计算机系统的技术发展速度是空前的
如何应对计算机系统的特殊性
-
迭代设计原则
-
小步前进:快速发现设计错误和思想错误,以便在错误不可挽回之前,轻易就可以更改或删除它们。
-
精心设计:即使单个步骤可能很小,仍然需要经过精心规划。
-
迎接反馈:反馈路径和提供反馈的积极激励措施都是设计的一部分。
-
研究错误:关键是从错误中吸取教训,而不是指责错误。
迭代在实施过程中遇到的障碍:
-
失去概念完整性
-
坏消息跑的慢
-
一旦发现模块化错误难以修复
-
-
-
保持简化原则(KIS)
计算机系统缺乏自然的物理限制来抑制其复杂性,因此设计者必须施加限制;否则设计者就有不堪重负的风险。
做到 KIS 的基本保证就是:设计者可以对提出需求的管理者或需求方说不
2.构造抽象计算机系统
2.1计算机系统中的三个抽象对象(存储、解释器、链路)
抽象定义:抽象是指抽取客观事物的一般的、本质的属性的思维方法。计算机的抽象可能有无数种,常见的抽象对象可以从不同的角度进行划分。计算机的功能划分为计算、记忆、传输;衡量计算复杂度的指标划分为时间、空间和消息;数字电路基本的组织方式划分为运算电路、存储电路、连接电路。
从三个方面分析抽象,第一,抽象了什么,明确定义被抽象的事物,例如硬盘、文件系统、数据库系统等;第二,抽象成什么,在了解抽象的对象后,对抽象的结果进行定义,抽象后有什么不同,定义了那些功能规格,如何访问等;第三,抽象的性质,抽象保留了哪些性质,规格有哪些指标等。
2.1.1存储
存储器,也称为存储设备,是计算机系统中的记忆设备,主要用来存放程序和数据。计算机中的全部信息,包括输入的原始数据、计算机程序、中间运行结果和最终运行结果,都保存在存储器中。
抽象了什么?
按信息的可保存性分类,存储设备分为易失性存储器和非易失性存储器
抽象成什么?
对于存储来说,必不可少的两个基本操作:WEITR 和 READ:
-
write ( name , value )
-
value ← read ( name )
抽象后应保持的性质?
-
写读连贯性 : 读的结果等于写的结果
-
并发操作的隔离 : 不会因为互相影响产生错误的结果
-
操作的原子性 : 或者完全做,或者全不做
存储的指标
时延
命名与寻址
2.1.2解释器
定义:解释器是执行组成计算的动作的活动部件,是计算机系统中的主动组件,例如 CPU、硬件控制器、显示控制器、操作系统、Java 字节码等。
解释器所有实现可以抽象为3部分:
-
指令引用 : 解释器如何找到下一条指令的位置
-
指令集 : 解释器从指令引用所指使的位置取回指令后发出下一步行动的指令.
-
环境引用 : 解释器执行当前指令动作的状态。
例子:通用处理器是一种解释器,处理器指令引用是程序计数器(PC),记录在处理器内部访问速度极快的寄存器中,环境引用包括一组内置的位置寻址寄存器,指令集包括执行计算的指令和存取的指令。通用处理器支持栈数据结构,用于实现过程调用,调用者将被调用者的参数压入栈,使用栈指针(SP)记录栈顶地址。同时通用处理器也支持中断,处理器检测到程序的异常就会跳转到异常处理程序,来自处理器外部的信号也会产生中断。
解释器的层次
解释器一般按照分层方式组织。最底层通常是硬件引擎,提供最基本的指令系统,上层的解释器提供更丰富或更专用的指令系统。
2.1.3通信链路
通信链路提供信息在物理分隔的组件间移动的途径。尽管实现通信链路的技术多种多样,但与存储器和解释器相同的是,通信链路也可以用简单的抽象来描述。
通信链路可抽象为两个基本操作:
-
send (link_name, outgoing_message_buffer)
-
receive (link_name, incoming_message_buffer)
通信链路为什么不能抽象为存储操作?
通信链路并非简单的通过线路将比特数组从一个内存复制到另一个内存,情况相对更加复杂,操作所需要的参数来源广泛使得发送和接收操作的完成时间无法预测,较差的环境在传输过程中会影响数据的完整性,异步操作导致消息的到来时间和大小无法提前获知,这些因素使得发送和接受操作的语义与读写操作相差甚远。
2.2命名与间接
名称 : 使外部观察者能够标识事物的术语
命名 : 对名称的创建和使用
2.2.1命名模型
命名方案3要素
-
名称空间
-
名称映射算法
-
绑定:建立关系
-
解析:用名找到值
-
-
值空间
-
value:对象或另一个名称
-
2.2.1名称解析
常见解析算法
-
表查找
-
递归查找(递归使用上下文引用)
-
多重查找
上下文引用
定义:编程和计算中用于确定代码、数据或资源在特定上下文中位置和含义的一种机制。它涉及使用特定的变量、标识符或名称来指向或访问相关的上下文信息。上下文可以是代码中的一个特定部分、一个数据结构、一个系统资源,或者任何需要被程序正确理解和处理的实体。
-
缺省方法:解析器提供
例如编程语言的关键字、启动扇区的地址或BIOS地址等。
-
显示方法:访问者提供
-
每访问者一个
-
每名称一个
-
路径名
定义:显式包含了自己所用的上下文引用的名称
多重查找
定义:透过分层上下文的搜索
2.2.3 名称比较
result ← compare (name1, name2) 的语义?
-
(语义1) 同一个名字?
-
(语义2)绑定到同一值?
-
(语义3)绑定到的值是相等的?
2.2.4 名称发现
常见方式
-
well-konw(总所周知)
-
广播
-
查询
-
广播查询
-
不同上下文的相同名称
2.3 用命名和分层构造抽象计算机系统
计算机系统常见的三层分层
-
硬件层
-
门、触发器、寄存器、内存单元、有限状态机
-
-
操作系统层
-
应用层
2.4UNIX文件系统
2.4.1块层(Block)
块
存储设备上固定大小的存储区,是操作系统中最小的逻辑储存单位文件由块组成
超级块
-
包含文件系统的关键元数据,如文件系统的类型、大小、状态、块大小、空闲和已使用块的数量等。
-
当计算机启动或文件系统被挂载时,操作系统会读取超级块以获取必要的信息来正确地初始化和访问文件系统。
-
记录位图和inode表的大小
2.4.2文件层
文件:由块组成,连续的块 = 字节的线性结构
2.4.3inode编号层(inode表)
inode:储存文件元信息
如何储存:建立inode表,通过inode编号访问inode
2.4.4文件名层(目录)
块可能移动,编号可能变,inode号难以记忆,因此提供易读的文件名,与inode编号对应。
特殊文件(目录)中存放文件名与inode编号关系
2.4.5路径名称层
目录inode也是文件,所以也要有名称
路径解析可用递归实现
2.4.6绝对路径层/
路径名称构造了许多目录树,要共享这些树,可以建立根目录(全局的名称上下文)
2.4.7符号链接层
如何将两个系统建立统一的文件系统?
-
创造新的inode类型:符号链接/软链接
mount ("/dev/fd1", "/flash")将fd1的root inode表示为/flash目录,从而可以跨文件系统访问
习题
说明计算机系统最常见的 3 个抽象的对象。为什么选择这 3 个抽象对象?
储存 解释器 链路
说明 3 个衡量计算复杂度的指标并举例。说明 3 个计算基本功能并举例。
时间复杂度 空间复杂度 消息复杂度
计算 存储 传输
如何分析一个抽象?尝试分析一个实际的例子。
抽象了什么:明确被抽象的事物。例如,硬盘。
抽象成什么:定义抽象后的结果和功能。例如,硬盘抽象成文件系统,提供文件的创建、读取、写入、删除等功能。
抽象的性质:保留的性质和规格。例如,文件系统需要保证数据的一致性、持久性和可访问性
举出 3 类存储硬件和 3 类存储高层抽象的例子
存储硬件
硬盘驱动器(HDD)
固态驱动器(SSD)
随机存取存储器(RAM)
存储高层抽象
文件系统:如 NTFS、ext4
数据库系统:如 MySQL、MongoDB
云存储服务:如 AWS S3、Google Drive
存储抽象中硬件层和高层相比,命名和操作的单元大小一般有什么差别?
硬件层:命名和操作的单元通常较小。例如,硬盘操作的基本单位是扇区(通常为 512 字节或 4 KB)。
高层:命名和操作的单元通常较大。例如,文件系统操作的基本单位是文件,数据库操作的基本单位是记录或表
存储抽象后要保持的两个重要性质是什么?分别是什么含义?为什么说存储抽象后要保持这两个性质,存在各种挑战?举出一些挑战的例子及一些应对措施。
写读连贯性 : 读的结果等于写的结果
并发操作的隔离 : 不会因为互相影响产生错误的结果
操作的原子性 : 或者完全做,或者全不做
名称是什么?举例说明为什么抽象和间接需要名称。举例说明名称如何带来间接。
名称是指用于标识对象的标签或符号。在计算机系统中,抽象和间接需要名称,因为:
抽象:通过名称,我们可以将复杂的系统分解为更简单的部分,使得每个部分可以独立处理。
间接:通过名称,我们可以引用对象,而不需要知道其具体实现细节,这样可以减少系统之间的耦合。
二、构造单机系统
3.命名
3.1命名需要考虑的设计因素
3.1.1名称冲突
如何产生的
模块间用名称共享子模块,模块使用不同命名上下文,不同模块间可能包含相同的组件,从而导致名称冲突
如何解决
闭包
是将上下文引用附加到对象上,而不修改其表式,这种方式不直接将对象的名称和对象本身相关联,而是与一个结构关联起来,在编程语言中,这个结构被称为“闭包”,该结构包括了原始对象及其上下文,它将过程定义与其定义时的命名上下文连接起来,通过闭包的方式,在大型应用程序的不同部分之间可以系统地实现共享命名对象。
3.1.2元数据
定义:指包含在对象内部或难以直接查找的关于对象的重要信息的数据。以文件为例,文件的元数据包括名称、唯一标识符、类型、时间、版本、所有者、witness 等
名称重载:由于文件系统的限制,文件名通常包含了与实际使用无关的元数据,这就是所谓的“名称重载”,命名方案会规定文件名的语法规则,以便支持将更多的元数据嵌入文件名称中。
名称重载可能是无害的,也可能破坏模块化设计和抽象原则,从而导致名称脆弱性
纯名称:没有任何重载的名称
3.1.3 地址
定义:在计算机系统中,地址是指一个物理位置或映射到物理位置的虚拟位置的名称。
地址可以以两种方式使用
-
作为具有常规命名操作的标识符
-
作为一个定位器使用
地址的易碎性:地址的指称物不能移动,因为地址的重载信息与位置相关
3.1.4唯一名称
定义:不会发生冲突的名称
如何生成:由命名方案为新创建的对象生成一个名称,而不是依赖创建者提出一个唯一的名称。一个简单的方案是分配连续的整数或足够精细的时间戳值来生成唯一的名称。
3.1.5 用户友好性
设计冲突
-
用户友好的名称易冲突,不易冲突的名称难以记忆
-
不同系统大小写敏感不同
更友好的方式
-
icon
-
超链接
3.1.6 生命周期
命名方案、名称、名称与值的绑定关系,以及被绑定的具体值,均可具备不同的生命周期。一般而言,名称和值本身的生命周期都相当长久。然而,将特定名称与特定值关联起来的绑定关系,其生命周期则可能相对短暂。
名称带来的问题:悬垂引用
值带来的问题:内存垃圾
3.2 示例:统一资源定位器(url)
url定义:统一资源定位符,用以标记数字资源
3.2.1名称发现
-
url初始来源于浏览器内置\文档\其他媒体
-
超链接:从url得到url
3.3 现实故事
3.3.1 碰撞抹去笑脸
3.3.2 脆弱的名称---域名
3.3.3 更脆弱的名称——邮政编码
4.模块化
模块化是降低计算机系统设计复杂度的重要方法之一。在通用系统的设计中,模块化是使用物理的方式划分,以空间的方式来隔离,并通过物理量的传递来进行交互
模块化的收益
-
便于维护
-
易于扩展
-
提高代码的可重用性
-
并行开发
-
便于测试和调试
4.1软模块化
定义:软模块化是指在大型程序中通过将其分解为相互调用的命名过程来创建模块化的一种方式。
比如c语言里面的各种函数就是一种模块化
C++中软模块化两种最为常用的工具
-
命名空间的软模块化
命名空间提供了一种将相关联的变量、函数、类等组织在一起的机制,从而实现软模块化。通过将相关功能放置在同一个命名空间下,可以将代码按照逻辑或功能进行划分,使得代码结构更加清晰。命名空间还可以避免命名冲突,提高代码的可读性和可维护性。
-
类的软模块化
类是面向对象编程中的核心概念,它将数据和行为封装在一起,提供了一种抽象的方式来描述对象。通过将相关功能封装在类中,可以实现软模块化,将系统划分为多个独立的模块,每个模块负责不同的功能
软模块化的问题
-
结构化编程仅在源码有效,而非执行时生命周期。
-
栈的问题 : 栈用于支持调用,并保持模块之间的状态独立性栈必须遵守栈法则和栈约定才能保持模块的隔离。
不遵守栈法则和约定就会破坏隔离
-
寄存器、内存的问题 : 汇编指令对寄存器的访问是没有限制的。在实际编程中,必须谨慎处理指针的赋值,以确保不会出现非法内存访问。否则,可能会导致数据损坏、安全漏洞或程序崩溃等问题。
问题产生的后果:
-
错误传播不可避免
-
可造成控制流破坏
-
导致调用者挂起或退出
软模块化是不可靠的模块化!
4.2 C/S架构
强制模块化是一种更为严格的模块化方法,它采用了客户端/服务端(C/S)架构模型。特别强调,两个不同的计算机,在 C/S 架构中,系统被划分为客户端模块和服务端模块,它们之间通过消息传递进行通信。这种模块化方法限制了模块之间的直接交互,使得系统更加健壮、安全和可扩展。
在 C/S 架构中,消息是客户端与服务端之间进行通信的唯一方式。客户端通过发送消息来请求服务,而服务端则通过处理这些消息来提供相应的服务。消息不仅是获得服务的方式,也是错误传播和安全破坏的唯一途径。因此,通过消息传递的方式可以实现模块之间的强耦合,同时避免了直接共享资源可能带来的故障传播和安全破坏的问题
强制模块化带来的收益
-
减少设计复杂度
-
可靠
-
安全
C/S是构建模块化系统\容错系统\安全系统的基本起点
4.3C/S通信——模块间的通信
定义:C/S(客户端/服务器)模型是一种常见的网络架构,其中客户端与服务器通过网络进行通信。该模型被广泛应用于各种网络应用中,包括万维网(WWW),电子邮件,文件传输等。C/S 模型的主要优点是将系统划分为客户端和服务器两部分,从而实现模块化设计,提高系统的可扩展性、可靠性和安全性
基本通信方式
-
HTTP 协议:C/S 模型中常用的通信协议。客户端向服务器发送 HTTP 请求,服务器返回相应的 HTTP 响应。常见于 Web 浏览器和 Web 服务器之间的交互。
-
远程过程调用(RPC):在客户端和服务器之间模拟本地过程调用,使远程通信变得像本地调用一样简单和透明。通过参数打包、消息发送、接收响应等步骤实现。
关键组件
-
URI 和 URL:用于标识服务器上的资源位置,客户端通过 URL 发送请求获取资源。
-
HTML:服务器通常向客户端发送 HTML 文档作为 HTTP 响应的一部分,客户端浏览器解析并显示内容。
-
HTTPD:运行在服务器端的软件,用于接收并处理客户端发送的 HTTP 请求,然后发送相应的 HTTP 响应。
通信过程
-
参数打包:将客户端请求的参数打包。
-
消息发送:客户端发送请求消息到服务器。
-
等待和接收:等待服务器处理并接收响应消息。
-
结果处理:解析服务器返回的响应结果。
4.4应用与案例
4.4.1 微服务
微服务(英语:Microservices)是一种软件架构风格,它是以专注于单一责任与功能的小型功能区块 (Small Building Blocks) 为基础,利用模块化的方式组合出复杂的大型应用程序,各功能区块使用与语言无关 (Lang 而且复杂的服务背后是使用简单 URI 来开放接口,任何服务,任何细粒都能被开放(exposed)。
4.4.2 NFS(Network File System)
该目标旨在实现能够挂载远程文件系统的系统,允许多个主机之间进行 N 对 N 的共享。
5.虚拟化
5.1什么是虚拟化
5.1.1虚拟化
定义:将计算机的各种实体资源,予以抽象转换后呈现出来可供分割、组合的一个或多个电脑配置环境。
-
复用:指在系统设计中利用现有的组件、模块或功能来创建新的系统。
-
聚合:将多个组件或模块组合在一起形成一个更大的系统。
-
模拟:通过模拟一个实体或系统的行为来模仿另一个实体或系统。
简单来说,复用就是只有一个,模仿多个;聚合是有很多个,模仿一个;模拟是有一个,模仿另一个。
类比现实中的例子来说,复用相当于 2 台餐桌共用 1 名服务员,聚合相当于 1 台餐桌使用2 名服务员,模拟相当于餐厅经理替代服务员。
5.1.2虚拟化对资源的要求
对于复用来说:要求任务可分割,并且分割的任务轮流使用资源。
对于聚合来说:要求任务可并行。
对于模拟来说:要求资源使用过程是完全可控的
5.1.3 虚拟化应用
虚拟化的主要用途
虚拟化应用可能在以下几方面体现,例如:在一台机器上通过虚拟化技术来同时运行多个客户操作系统;通过虚拟化技术的复用,将错误限制在一个客户操作系统中;虚拟化出更加简单的接口后,简化操作系统的开发。
-
多路复用
通过将物理资源虚拟化为多个独立的虚拟实例,同时运行多个应用程序或服务,提高资源的利用率和效率,实现资源的共享和弹性扩展。
-
聚合
通过将多个物理资源虚拟化并集成在一个虚拟环境中,形成一个更强大和功能丰富的系统。实现虚拟化在资源的统一管理和协同工作。
-
模拟
通过虚拟化软件和工具,模拟出虚拟环境,使其具有与实际环境相似的功能。这种模拟的能力使得虚拟化在软件测试、应用程序开发和系统仿真等领域得到广泛应用,实现对实际环境的模拟和评估。
实际例子
虚拟化收益
-
互相隔离逻辑错误(但不能隔离物理错误)
-
一定程度上隔离攻击(隔离效果低于物理隔离)
5.2 虚拟计算机抽象
在实现虚拟计算机时,主要的挑战是找到适合构建它们的正确抽象。
5.2.1虚拟处理器
虚拟化计算机的第一步
定义
虚拟处理器是用环境引用和指令引用找到正确的指令,再对其进行解释,通过环境引用找到操作数加上指令集中的操作码,对指令进行执行,如果没有中断,则直接找到下一条指令循环上面的过程;如果产生中断,则要修改指令引用和环境引用,再去新的环境找到下一条指令。
线程
抽象后的执行流称为线程。线程是一种封装了活动计算的执行状态的抽象。它封装了执行计算的概念解释器的状态,由于每个线程都以为自己拥有自己的处理器,所以线程是虚拟处理器。我们可以把线程的结构理解为栈,第一个栈帧包含的是要返回到调用该函数的代码位置,于是线程的关键不是内存中的代码和数据,而在于栈帧的设立
5.2.2 虚拟内存
由于线程共用一份内存会导致错误传播、代码约束、不安全等缺点,虚拟内存也是虚拟计算机的必然选择
前面所说的线程虚拟化,是为了虚拟出多个系统,按 C/S 的方式工作,是强制模块化前提。而内存虚拟化,则是解决这些系统之间的资源隔离,是强制模块化的保证。
5.2.3 有界缓冲区
一个线程可以调用发送操作(send),它尝试将提供的消息插入到有界消息缓冲区中。如果有界缓冲区已满,发送线程将等待,直到有空间可用。一个线程可以调用接收操作(receive)从缓冲区中检索消息;如果没有消息,调用线程将等待。通过使用发送、接收和有界缓冲区,可以实现远程过程调用,并在运行在同一台物理计算机上的不同虚拟计算机之间强制实施强模块化
5.2.4 操作系统接口
使用小型概念OS来具体说明这些抽象概念,原理是揭示真实 OS 中的某些机制,并且与具体 OS 无关且相对简单。
5.3模拟器与虚拟机
另一种方法是提供与某些物理硬件完全相同的接口,这个接口与某些物理硬件完全一样。通过这种方法,可以通过为每个应用程序提供其自己的物理硬件实例来实现模块化。
6.虚拟链路与消息序列协调
操作系统设计者为虚拟通信链路开发了许多抽象概念。其中一个流行的抽象概念是管道,它允许两个程序使用控制文件系统调用接口的程序进行通信。
6.1 基本原语
关键挑战:消息序列协调
有界缓冲区的实现需要发送线程和接收线程之间的协调,因为线程可能需要等待缓冲区空间可用或消息到达。多年来,不同领域的研究人员开发了两种截然不同的线程协调方法。一种方法通常由操作系统设计者采用,它假定程序员是一个无所不知的天才,不会犯任何错误。另一种方法通常由数据库设计者采用,它假定程序员只是一个凡人,因此它为协调的正确性提供了强大的自动支持,但在灵活性方面付出了一定代价。
实现所需的正确性假设
-
收发1个线程,不支持多发/多接受
-
线程拥有自己的处理器
-
in和out不溢出
-
读/写连贯性得到保证
-
时间先后原子性得到保证
-
程序指令没有因优化被重新排列
6.2 竞态条件
它旨在描述一个系统或者进程的输出依赖于不受控制的事件出现顺序或者出现时机。此词源自于两个信号试着彼此竞争,来影响谁先输出。举例来说,如果计算机中的两个进程同时试图修改一个共享内存的内容,在没有并发控制的情况下,最后的结果依赖于两个进程的执行顺序与时机。而且如果发生了并发访问冲突,则最后的结果是不正确的。
6.3 锁与死锁
6.3.1锁
锁变量仅仅作为一个标志,为了实现正确的协调,所有线程都必须遵守一个额外的约定:除非它们持有锁,否则它们不能对共享变量执行操作。如果任何线程未能遵守该约定,则可能会出现不受欢迎的交互和竞争
为了确保并发线程的正确性,设计者必须识别所有潜在的竞态,并仔细插入获取和释放的调用以防止它们。如果锁定语句不能确保共享变量上的多步操作显示为单步操作,那么程序就可能出现竞态条件。
6.3.2 死锁
循环死锁:每个人都在等待其他人释放资源
死锁的实现获取和释放:获取和释放的正确实现必须执行单获取协议。多个线程可以同时尝试获取锁,但只有一个线程应该成功。这个需求使得锁的实现具有挑战性。本质上,在程序中必须确保获取本身是一个事前或事后的行为。
如何进行加锁?
-
检验队列大小等于 0(或 N)的时候,加锁——释放,再检验。
-
检验队列大小不等于 0(或 N),操作消息和改变 in/out 三个动作,加锁——释放
7.内存模拟化与虚拟内存
7.1分段分级内存管理
Q:如何决定应用程序的哪些部分使用小而快的存储,哪些部分使用大而慢的存储?
在内存设计中,我们需要在延迟和容量之间做出折中。较低延迟的存储单元离处理器较近,但容量有限。而较高延迟的存储单元位于较远的位置,但容量更大。我们需要根据具体的应用需求和性能要求来平衡延迟和容量,以获得最佳的性能和效率。
7.1.1内存特征
-
容量
-
平均随机访问延迟
-
成本
-
吞吐率
-
存储单元大小
7.1.2 利用虚拟内存管理多层存储
大多数现代化内存管理都采用操作系统管理的方式
7.1.3 分析多级存储系统
快速级别的设备被称为主设备,而较慢速级别的设备被称为辅助设备
在虚拟内存系统中,主设备通常是寄存器或者由 SRAM 构成的 Cache;而辅助设备可以是较慢的RAM 或磁盘。
7.14 存储访问的局部性与工作集
访存局部性
大部分应用程序很难找出既能放在主存储设备上,又能集中 99%以上的访问集合。但是,在很多情况下,大多数访问存在相当长的一段时间内都集中于一小部分地址。当程序继续执行时,这些集中访问的地址集合会发生变化,但是地址集合中的地址数量通常仍然保持较小。程序访存行为中的这种集中在不渐变化的小地址集合上的特征使得多级存储系统成为可能。如果程序具有这种集中访存特性,我们称其具有访存局部性
为了分析这种情况,我们把程序的执行想象成生成一个虛拟地址流,我们称之为访存序列。
访存序列中有两类访存局部性:
-
时间局部性:访存序列中包含了多个临近的对同一地址的访问。
-
空间局部性:访存序列中包含了多个临近的对相近地址的访问
7.2 内核
内核模式定义:内核模式(Kernel Mode)是操作系统运行的一种特权模式。在这种模式下,操作系统内核拥有对所有计算机资源的完全访问权限,包括硬件设备、内存和处理器。
内核模式与用户模式(User Mode)相对,用户模式是应用程序运行的模式,权限较低,仅能访问分配给它的资源。
内核定义:运行在内核模式下的模块的集合通常被称为内核程序,简称为内核。
内核是一个可信的中介机构,因为它是唯—个可以运行特权指令的程序,同时应用程序模块也依赖于内核模块进行正确操作,操作系统的绝大多数代码都运行在内核模式下。
如何进入内核态?
-
采用门方案
进入内核态需要为原子操作,不能被中断!
微内核
在传统的宏内核结构中,如果文件管理器模块的程序员犯了一个错误,可能会影响到与文件系统无关的内核数据结构,导致整个内核功能受到影响。
为了解决这个问题,微内核体系结构将操作系统组织成了一个客户端/服务器的形式,应用了强制模块化的思想。在微内核中,操作系统的不同模块以独立的用户空间进程运行,而不是作为宏内核的一部分。微内核本身只实现了最基本的抽象,包括模块的容器、线程的运行和模块之间的虚拟
微内核架构提供了模块之间的隔离性。每个模块在自己的用户空间进程中运行,拥有独立的资源和权限。这样一来,一个模块的错误不会意外地修改其他模块的数据结构。
宏内核
但实际上,广泛使用的操作系统很少有以最纯净的形式采用微内核架构。大多数广泛使用的操作系统仍然使用宏内核。许多关键服务在内核模式下运行,只有少数一部分在用户模式下运行。
宏内核优点
-
对于一些关键服务,将其放置在内核态或用户态都可以。但是,无论哪种方式,系统都必须保持可用。
-
一些服务需要在多个模块之间共享,这在宏内核中更容易实现。例如,最近访问的文件数据的缓存可以在所有模块之间共享。
-
有一些服务的性能是关键因素,SEND 和 RECEIVE 管理程序接口调用的开销可能过大而不能将子系统分成更小的模块,并且在将一个模块分开。
-
宏内核系统也可以有微内核系统方便调试的特性,但前提是宏内核系统上有很好的内核调试工具。
-
对现有的内核程序重新组织可能非常困难。特别是如果系统已经能够正常工作并且大部分错误已经修复,那么没有必要对已经运行良好的内核进行更改。此时,微内核的调试优势变得不那么重要,而 SEND 和 RECEIVE 等管理程序调用的开销成为更重要的问题。
7.3 虚拟内存
为了减轻程序员在内存管理方面的负担,并提高程序的性能,现代计算机系统采用了内存虚拟化的技术。
内存虚拟化通过引入两个主要特性来实现:
-
虚拟地址
-
虚拟地址空间
7.3.1 虚拟地址
内存管理器可以将多个虚拟地址转换为相同的物理地址,并且赋予它们不同的权限。内存管理器可以为线程分配虚拟地址,但直到线程引用其中一个虚拟地址时才延迟分配物理内存
三、构造互联系统
四、构造更好的系统
伦理分析过程
批判性和反思性地审查伦理事件的不同观点和论据,获得有效的结论(决策)
-
识别伦理事项
-
找到伦理论据
‣ 一些例子:漠视生命、伤及无辜、损失对于不同群体的不平均……
-
做出伦理判断
‣ 伦理判断属于价值判断,即基于论据的正确性、优先性的判断