现代操作系统::多处理器系统

多处理器系统 多处理机 共享存储器多处理机是这样一种计算机系统,其两个或更多的 CPU 全部共享访问一个公用的 RAM。 CPU 可对存储器的某个字写入某个值,然后读回该字,并得到一个不同的值。在进行恰当组织时,这种性质构成了处理器间通信的基础:一个 CPU 向存储器写人某些数据而另一个读取这些数据。 多处理器硬件 所有的多处理机都具有每个 CPUi 可访问全部存储器的性质 UMA 多处理器读出每个存储器字的速度是一样快的,NUMA 没有这种特性 基于总线的 UMA 多处理器结构 两个或更多的 CPU 以及一个或多个存储器模块都使用同一个总线进行通信 当一个 CPU 需要读一个存储器字 (memory word) 时,它首先检查总线忙否。 如果总线空闲,该 CPU 把所需字的地址放到总线上,发出若干控制信号,然后等待存储器把所需的字放到总线上。当 CPU 核数上升时,效率下降严重。 解决方案是为每个 CPU 添加一个高速缓存 高速缓存可以位于 CPU 芯片的内部、 CPU 附近、在处理器板上或所有这三种方式的组合 高速缓存 不以单个字为基础,而是以 32 字节或 64 字节块为基础 高速缓存一致性协议 CPU 试图在一个或多个远程高速缓存中写入一个字,总线硬件检测到写,并把一个信号放到总线上通知所有其他的高速缓存。如果其他高速缓存有个“干净"的副本,也就是同存储器内容完全一样的副本,那么它们可以丢弃该副本井让写者在修改之前从存储器取出高速缓存块。如果某些其他高速缓存有"脏"(被修改过)副本,它必须在处理写之前把数据写回存储器或者把它通过总线直接传送到写者上。 基于交叉开关的 UMA 多处理机 连接 n 个 CPU 到 K 个存储器的最简单的电路是交叉开关 水平线(进线)和垂直线(出线)的每个相交位咒上是一个交叉点 (crosspoint)。交叉点是一个小 的电子开关,具体取决千水平线和垂直线是否偕要连接。 非阻塞网络 使用多级交换网络的 UMA 多处理机 有一种完全不同的、基于简单 2 x 2 开关的多处理机设计。 这个开关有两个输入和两个输出。到达任意一个输入线的消息可以被交换至任意一个输出线上。就我们的目标而言,消息可由四个部分组成。Module (模块)域指明使用哪个存储器。Address (地址)域指定在模块中的地址。Opcode (操作码)给定了操作,如 READ 或 WRITE。最后,在可选的 Value (值)域中可包含一个操作数,比如一个要被 WRITE 写入的 32 位字。该开关检查 Module 域并利用它确定消息是应该送给 X 还是发送给 Y。 这个 2 x 2 开关可有多种使用方式,用以构建大型的多级交换网络。 有一种是简单经济的 omega 网络, Omega 网络的接线模式常被称作全混洗(perfect shuffle)。 接着看看 Omega 网络是如何工作的,假设 CPU 011 打算从存储器模块 110 读取一个字。CPU 发送 READ 消息给开关 1D, 它在 Module 域包含 110。ID 开关取 110 的首位(最左位)并用它进行路由处理。0 路由到上端输出,而 1 的路由到下端,由于该位为 1, 所以消息通过低端输出被路由到 2D。 所有的第二级开关,包括 2D,取用第二个比特位进行路由。这.位还是 1,所以消息通过低端输出转发到 3D。在这里对第三位进行测试,结果发现是 0。于是,消息送往上端输出,并达到所期望的存储器 110。 在消息通过交换网络之后,模块号的左端的位就不再需要了。它们可以有很好的用途,可以用来记录入线编号,这样,应答消息可以找到返回路径。对于路径 a,入线编号分别是 0 (向上输入到 1D)、1(低输入到 2D),1(低输入到 3D)、使用 011 作为应答路由,只要从右向左读出每位即可。 阻塞网络 NUMA 多处理机 所有 NUMA 机器都具有以下三种关键特性,它们是 NUMA 与其他多处理机的主要区别: 具有对所有 CPU 都可见的单个地址空间。 通过 LOAD 和 STORE 指令访问远程存储器。 访问远程存储器慢于访问本地存储器。 在对远程存储器的访问时间不被隐藏时(因为没有高速缓存),系统被称为 NC-NUMA (No Cache NUMA,无高速缓存 NUMA)。在有一致性高速缓存时,系统被称为 CC-NUMA (Cache Coherent NUMA,高速缓存一致 NUMA)。 目前构造大型 CC-NUMA 多处理机最常见的方法是基于目录的多处理机(directory-based multiprocessor)。其基本思想是,维护个数据库来记录高速缓存行的位置及其状态。当一个高速缓存行被引用时,就查询数据库找出高速缓存行的位置以及它是“干净”的还是“脏”的。由于每条访问存储器的指令都必须查询这个数据库,所以它必须配有极高速的专用硬件。 多核芯片 CPU 拥有多于一个内核 CPU 可能共享高速缓存或者不共享,但是它们都共享内存 特殊的硬件电路可以确保在一个字同时出现在两个或者多个的高速缓存中的情况下,当其中某个 CPU 修改了该字,所有其他高速缓存中的该字都会被自动地并且原子性地删除来确保一致性。这个过程称为窥探 众核芯片 众核芯片是指包括几十、几百甚至成千上万个核心的多核处理器 保持缓存一致性的机制会变得非常复杂和昂贵,一致性壁垒 一些工程师认为,唯一已证明可适用于众核的编程模型是采用消息传递和分布式内存实现的。 成千上万的核心数现在已不再那么少见了,图形处理单元(GPU)作为当今最为常见的众核。 于几乎任何一台非嵌入式并且有显示器的计算机系统中。GPU 是一个拥有专用内存和成千上万个微小核的处理器则。因而它们十分擅长进行像图形程序渲染多边形这样的大量并行的小规模计算,而不太擅长串行任务,同时也很难对它们编程。 异构多核 一些芯片会把一个 GPU 和一些通用处理器核封装在一起,许多片上系统在通用处理器核之外还包括一个或多个特殊用途的处理器。在一块芯片上封装了不同类型的处理器的系统袚统称为异构多核处理器 在多核上编程 当前的编程语言井不适合编写高度并行的程序,好的编译器和调试工具也很少见,程序员很少有井行编程的经验 同步、消除资源竞争条件和避免死锁等问题就像噩梦一般 应用场景 多处理机操作系统类型 每个 CPU 有自己的操作系统 允许所有的 CPU 共享操作系统的代码,而且只需要提供数据的私有副本 四个潜在的问题 首先,在一个进程进行系统调用时,该系统调用是在本机的 CPU 上被捕获并处理的,并使用操作系统表中的数据结构。 其次,因为每个操作系统都有自己的表,那么它也有自己的进程集合,通过自身调度这些进程。 第三,没有共享物理页面。会出现如下的情形:在 CPU2 不断地进行页面调度时 CPU1 却有多余的页面。由于内存分配是固定的,所以 CPU 2 无法向 CPU 1 借用页面。 第四,也是最坏的情形,如果操作系统维护近期使用过的磁盘块的缓冲区高速缓存,每个操作系统都独自进行这种维护工作,因此,可能出现某一修改过的磁盘块同时存在于多个缓冲区高速缓存的情况,这将会导致不一致性的结果。避免这一问题的唯一途径是,取消缓冲区高速缓存。 主从多机处理机 在这种模型中,操作系统的一个副本及其数据表都在 CPU 1 上,而不是在其他所有 CPU 上。为了在该 CPU 1 上进行处理,所有的系统调用都重定向到 CPU 1 上。如果有剩余的 CPU 时间,还可以在 CPU 1 上运行用户进程。这种模型称为主从模型( master-slave), 因为 CPU 1 是主 CPU,而其他的都是从属 CPU。 主从模型解决了在第一种模型中的多数问题。有单一的数据结构(如一个链表或者一组优先级链表)用来记录就绪进程。当某个 CPU 空闲下来时,它向 CPU 1 上的操作系统请求一个进程运行,并被分配一个进程。这样,就不会出现一个 CPU 空闲而另一个过载的情形。类似地,可在所有的进程中动态地分配页面,而且只有一个缓冲区高速缓存,所以决不会出现不一致的情形。 这个模型的问题是,如果有很多的 CPU, 主 CPU 会变成一个瓶颈。 对称对处理机 消除了上述的不对称性。在存储器中有操作系统的一个副本,但任何 CPU 都可以运行它。在有系统调用时,进行系统调用的 CPU 陷入内核并处理系统调用 任何 CPU 都可以运行操作系统,但在任一时刻只有一个 CPU 可运行操作系统 可以把操作系统分割成互不影响的临界区。每个临界区由其互斥信号量保护,所以一次只有一个 CPU 可执行它。采用这种方式,可以实现更多的井行操作。 多处理机同步 如果一个进程在单处理机(仅含一个 CPU)中需要访问一些内核临界表的系统调用,那么内核代码在接触该表之前可以先禁止中断。然后它继续工作,在相关工作完成之前,不会有任何其他的进程溜进来访问该表。 在多处理机中,禁止中断的操作只影响到完成禁止中断操作的这个 CPU,其他的 CPU 继续运行并且可以访问临界表。因此,必须采用一种合适的互斥信号量协议,而且所有的 CPU 都遵守该协议以保证互斥工作的进行。 任何实用的互斥信号量协议的核心都是一条特殊指令, 该指令允许检测一个存储器字并以一种不可见的操作设置。指令 TSL (Test and Set Lock 做的是,读出一个存储器字并把它存储在一个寄存器中。同时,它对该存储器字写入一个 1 (或某些非零值)。当然,这需要两个总线周期来完成存储器的读写。在单处理机中,只要该指令不被中途中断,TSL 指令就始终照常工作。 为了阻止多个 CPU 同时访问同一个存储器字,SL 指令必须首先锁住总线,阻止其他的 CPU 访问它,然后进行存储器的读写访问,再解锁总线。 如果正确地实现和使用 TSL,它能够保证互斥机制正常工作。但是这种互斥方法使用了自旋锁(spin lock), 因为请求的 CPU 只是在原地尽可能快地对锁进行循环测试。这样做不仅完全浪费了提出请求的各个 CPU 的时间,而且还给总线或存储器增加了大量的负载,严重地降低了所有其他 CPU 从事正常工作的速度。 在获取锁之前先观察锁是否空闲,可以减少竞争,避免高速缓存颠簸。 另一个减少总线流量的方式是使用著名的以太网二进制指数回退算法 一个更好的想法是,让每个打算获得互斥信号量的 CPU 都拥有各自用于测试的私有锁变且 自旋与同步 假设一些 CPU 是空闲的,需要访问共享的就绪链表(ready list) 以便选择一个进程运行。如果就绪链表被锁住了,CPU 必须保持等待直到能够访问该就绪链表。 然而,在另外一些情形中,却存在着别的选择。例如,如果在一个 CPU 中的某些线程需要访问文件系统缓冲区高速缓存,而该文件系统缓冲区高速缓存正好锁住了,那么 CPU 可以决定切换至另外一个线程而不是等待。 假设自旋和进行线程切换都是可行的选择,则可进行如下的权衡。自旋直接浪费了 CPU 周期。重复地测试锁并不是高效的工作。不过,切换也浪费了 CPU 周期,因为必须保存当前线程的状态,必须获得保护就绪链表的锁,还必须选择一个线程,必须装入其状态,并且使其开始运行。花费在这两个线程间来回切换和所有高速缓存未命中的周期时间都浪费了。 有一种设计是总是进行自旋。 第二种设计 方案则总是进行切换。 而第三种设计方案是每当遇到一个锁住的互斥信号量时,就单独做出决定。在必须做出决定的时刻,并不知道自旋和切换哪种方案更好,但是对于任何给定的系统,有可能对其所有的有关活动进行跟踪,并且随后进行离线分析。然后就可以确定哪个决定最好及在最好情形下所浪费的时间。 得出来了这样一个模型: 一个未能获得互斥信号量的线程自旋一段时间。 如果时间超过某个阈值,则进行切换。在某些情形下,该阈值是一个定值,典型值是切换至另一个线程再切换回来的开销。在另一些情形下,该阈值是动态变化的,它取决于所观察到的等待互斥信号量的历史信息。 多处理机调度 线程是内核线程还是用户线程至关重要。如果线程是由用户空间库维护的,而对内核不可见,那么调度一如既往的基于单个进程。如果内核并不知道线程的存在,它就不能调度线程。 对内核线程来说,情况有所不同。在这种情况下所有线程均是内核可见的,内核可以选择一个进程的任一线程。 在单处理机中,调度是一维的。唯一必须(不断重复地)回答的问题是:“接下来运行的线程应该是哪一个?”而在多处理机中,调度是二维的。调度程序必须决定哪一个进程运行以及在哪一个 CPU 上运行。这个在多处理机中增加的维数大大增加了调度的复杂性。 另一个造成复杂性的因素是,在有些系统中所有的线程是不相关的,它们属于不同的进程,彼此无关。而在另外一些系统中它们是成组的,同属于同一个应用并且协同工作。前一种情形的例子是服务器系统,其中独立的用户运行相互独立的进程。这些不同进程的线程之间没有关系,后一种情形通常出现在程序开发环境中。 分时 让我们首先讨论调度独立线程的情况。处理独立线程的最简单算法是,为就绪线程维护一个系统级的数据结构,它可能只是一个链表,但更多的情况下可能是对应不同优先级一个链表集合。 由所有 CPU 使用的单个调度数据结构分时共享这些 CPU,正如它们在一个单处理机系统中那样。它还支持自动负载平衡,因为决不会出现一个 CPU 空闲而其他 CPU 过载的情况。不过这一方法有两个缺点,一个是随着 CPU 数量增加所引起的对调度数据结构的潜在竞争,二是当线程由于 I/O 阻塞时所引起上下文切换的开销(overhead)。 为了避免这种异常情况,一些系统采用智能调度(smartscheduling)的方法,其中,获得了自旋锁的线程设置一个进程范围内的标志以表示它目前拥有了一个自旋锁。 当它释放该自旋锁时,就清除这个标志。这样调度程序就不会停止持有自旋锁的线程,相反,调度程序会给予稍微多一些的时间让该线程完成临界区内的工作并释放自旋锁。 有些多处理机考虑了这一因素,并使用了所谓亲和调度。其基本思想是,尽量使一个线程在它前一次运行过的同一个 CPU 上运行。创建这种亲和力(affinity)的一种途径是采用一种两级调度算法(two-level scheduling algorithm)。 在一个线程创建时,两级调度算法有三个优点。 它把负载大致平均地分配在可用的 CPU 上; 它尽可能发挥了高速缓存亲和力的优势 通过为每个 CPU 提供一个私有的就绪线程链表,使得对就绪线程链表的竞争减到了最小,因为试图使用另一个 CPU 的就绪线程链表的机会相对较小。 空间共享 在多个 CPU 上同时调度多个线程称为空间共享(space sharing)。 最简单的空间共享算法是这样工作的。假设一组相关的线程是一次性创建的。在其创建的时刻,调度程序检查是否有同线程数量一样多的空闲 CPU 存在。如果有,每个线程获得各自专用的 CPU(非多道程序处理)并且都开始运行。如果没有足够的 CPU,就没有线程开始运行,直到有足够的 CPU 时为止。 在单处理机系统中,最短作业优先是批处理调度中知名的算法。在多处理机系统中类似的算法是,选择需要最少的 CPU 周期数的线程,也就是其 CPU 周期数*运行时间最小的线程为候选线程。然而,在实际中,这一信息很难得到,因此该算法难以实现。事实上,研究表明,要胜过先来先服务算法是非常困难的。 在这个简单的分区模型中,一个线程请求一定数量的 CPU,然后或者全部得到它们或者一直等到有足够数量的 CPU 可用为止。 另一种处理方式是主动地管理线程的并行度。管理并行度的一种途径是使用一个中心服务器,用它跟踪哪些线程正在运行,哪些线程希望运行以及所需 CPU 的最小和最大数量。 每个应用程序周期性地询问中心服务器有多少个 CPU 可用。然后它调整线程的数量以符合可用的数量。 群调度 空间共享的一个明显优点是消除了多道程序设计,从而消除了上下文切换的开销。但是,一个同样明显的缺点是当 CPU 被阻塞或根本无事可做时时间被浪费了,只有等到其再次就绪。于是,人们寻找既可以调度时间又可以调度空间的算法,特别是对于要创建多个线程而这些线程通常需要彼此通信的线程。 这一问题的解决方案是群调度(gang scheduling), 它是协同调度的发展产物。群调度由三个部分组成: 把一组相关线程作为一个单位,即一个群(gang), 一起调度。 一个群中的所有成员在不同的分时 CPU 上同时运行。 群中的所有成员共同开始和结束其时间片。 使群调度正确工作的关键是,同步调度所有的 CPU。这意味着把时间划分为离散的时间片。 多计算机 多计算机是紧耦合 CPU,不共享存储器。每台计算机有自己的存储器。众所周知,这些系统有各种其他的名称,如机群计算机以及工作站机群。云计环服务都是建立在多计算机上,因为它们需要大的计算能力。 多计算机硬件 一个多计算机系统的基本节点包括一个 CPU、存储器、一个网络接口,有时还有一个硬盘。节点可以封装在标准的 PC 机箱中,不过通常没有图像适配卡、显示器、键盘和鼠标等。有时这种配置被称为无主工作站、因为没有用户。有用户的工作站逻辑上应该对应地被叫作有主工作站 互联技术 在每个节点上有一块网卡,带有一根或两根从网卡上接出的电缆(或光纤)。这些电缆或者连到其他的节点上,或者连到交换机上 在多计算机中可采用两种交换机制 在存储转发包交换,由源节点的网络接口卡注入第一个交换机的包组成。比特串一次进来一位,当整个包到达一个输入缓冲区时,它被复制到沿着其路径通向下一个交换机的队列当中。当数据包到达目标节点所连接的交换机时,该数据包被复制到目标节点的网络接口卡,并最终到达其 RAM. 另一种交换机制是电路交换,它包括由第一个交换机建立的,通过所有交换机而到达目标交换机的一条路径。一且该路径建立起来,比特流就从源到目的地通过整个路径不断地尽快输送。在所涉及的交换机中,没有中间缓冲。电路交换需要有一个建立阶段,它需要一点时间,但是一且建立完成,速度就很快。在包发送完毕之后,该路径必须被拆除。电路交换的一种变种称为虫孔路由,它把每个包拆成子包,井允许第一个子包在整个路径还没有完全建立之前就开始流动。 网络接口 接口板上可以有一个或多个 DMA 通道,甚至在板上有一个完整的 CPU (乃至多个 CPU)。通过请求在系统总线上的块传送, DMA 通道可以在接口板和主 RAM 之间以非常高的速率复制包,因而可以一次性传送若干字而不需要为每个字分别请求总线 很多接口板上有一个完整的 CPU, 可能另外还有一个或多个 DMA 通道。它们被称为网络处理器, 并且其功能日趋强大。这种设计意味着主 CPU 将一些工作分给了网卡,诸如处理可靠的传送、多播、压缩/解压缩、加密/解密以及在多进程系统中处理安全事务等。但是,有两个 CPU 则意味着它们必须同步,以避免竞争条件的发生,这将增加额外的开销,井且对千操作系统来说意味着要承担更多的工作。 跨层复制数据是安全的,但不一定高效 低层通信软件 在多计算机系统中高性能通信的敌人是对包的过度复制 为了避免这种对性能的影响,不少多计算机把接口板映射到用户空间,井允许用户进程直接把包送到卡上,而不需要内核的参与 如果在节点上有若于个进程运行而且需要访问网络以发送包,一个解决方案是,把接口板映射到所有需要它的进程中去,但是这样做就需要有一个机制用以避免竞争 内核本身会经常需要访问互连网络,最简单的设计是使用两块网络接口板,一块映射到用户空间供应用程序使用,另一块映射到内核空间供操作系统使用。许多多计算机就正是这样做的. 另一方面,较新的网络接口通常是多队列的,这意味若它们有多个缓冲区可以有效地支持多个用户.例如, Intel 1350 系列网卡具有 8 个发送和 8 个接收队列,可虚拟化为许多虚拟端口 节点至网络接口通信 将包送到接口板上。最快的方法是使用板上的 DMA 芯片直接将它们从 RAM 复制到板上 这种方式的问题是,DMA 可以使用物理地址而不是虚拟地址,井且独立干 CPU 运行,除非存大 I/0 MMU。 另外,如果操作系统决定替换一个页面.而 DMA 芯片正在从该页面复制一个包,就会传送错误的数据。然而更加糟糕的是,如果操作系统在替换某一个页面的同时 DMA 芯片正在把一个包复制进该页面,结果不仅进来的包会丢失,无辜的存储器页面也会被毁坏,这可能会带来灾难性的后果。 可采用一类将页面钉住和释放的系统调用,把有关页面标记成暂时不可交换的。 远程直接内存访间 降低数据的复制品都需要很大代价。为了应对这一问题,一些网络接口支持远程直接内存访问 (RMDA) 技术,允许一台机器直接访问另一台机器的内存。RMDA 不需要操作系统的参与,直接从应用的内存空间中读取或写人数据. 用户层通信软件 在多计算机中,不同 CPU 上的进程通过互相发送消息实现通信。在最简单的情况下,这种消息传送是暴露给用户进程的。换句话说,操作系统提供了一种发送和接收消息的途径,而库过程使得这些低层的调用对用户进程可用。在较复杂的情形下,通过使得远程通信看起来像过程调用的办法,将实际的消息传递对用户隐藏起来 远程过程调用 允许程序调用位于其他 CPU 中的过程。当机器 1 的进程调用机器 2 的过程时,在机器 1 中的调用进程被挂起,在机器 2 中被调用的过程执行。可以在参数中传递从调用者到被调用者的信息,并且可在过程的处理结果中返回信息。根本不存在对程序员可见的消息传递或 I/O。 这种技术即是所谓的远程过程调用,并且已经成为大多计算机的软件的基础。习惯上,称发出调用的过程为客户机,而称被调用的过程为服务器 分布式共享存储器 每台机器有其自己的虚拟内存和页表。当一个 CPU 在一个它并不拥有的页面上进行 LOAD 和 STORE 时,会陷入到操作系统当中。然后操作系统对该页面进行定位,并请求当前持有该页面的 CPU 解除对该页面的映射并通过互连网络发送该页面。在该页面到达时,页面被映射进来,于是出错指令重新启动。事实上,操作系统只是从远程 RAM 中而不是从本地磁盘中满足了这个缺页异常。对用户而言,机器看起来拥有共享存储器。 多计算机调度 维护就绪进程的一个中心链表,每个进程只能在其当前所在的 CPU 上运行。不过,当创建一个新进程时,存在着一个决定将其放在哪里的选择,例如,从平衡负载的考虑出发。 负载平衡 图论确定算法:有一类被广泛研究的算法用干下面这样一个系统,该系统包含已知 CPU 和存储器需求的进程,以及给出每对进程之间平均流量的已知矩阵。如果进程的数众大于 CPU 的数量 k, 则必须把若干个进程分配给每个 CPU。其想法是以最小的网络流量完成这个分配工作。 发送者发起的分布式启发算法:当进程创建时,它就运行在创建它的节点上,除非该节点过载了。过载节点的度量可能涉及太多的进程,过大的工作集,或者其他度量。如果过载了,该节点随机机选择另一个节点井询问它的负载情况。如果被探查的节点负载低于某个阈值,就将新的进程送到该节点上。如果不是,则选择另一个机器探查。探查工作并不会永远进行下去。在 N 次探查之内,如果没有找到合适的主机,算法就终止,且进程继续在原有的机器上运行。整个算法的思想是负载较重的节点试图甩掉超额的工作。应该看到在负载重的条件下,所有的机器都会持续地对其他机器进行探查,徒劳地试配找到一台愿意接收更多工作的机器。几乎没有进程能够被卸载,可是这样的尝试会带来巨大的开销 。 接收者发起的分布式启发算法:在这个算法中,只要有一个进程结束,系统就检查是否有足够的工作可做。如果不是,它随机选择某台机器井要求它提供工作。如果该台机器没有可提供的工作,会接若询问第二台,然后是第三台机器。如果在 N 次探查之后,还是没有找到工作,该节点暂时停止询问,去做任何已经安排好的工作,而在下一个进程结束之后机器会再次进行询问。如果没有可做的工作,机器就开始空闲。在经过固定的时间间隔之后,它又开始探查。 分布式系统 这些系统与多计算机类似,每个节点都有自己的私有存储器,整个系统中没有共享的物理存储器。但是,分布式系统与多计算机相比,耦合度更加松散。 分布式系统添加在其底层网络上的是一些通用范型(模型),它们提供了一种统一的方法来观察整个系统。分布式系统想要做的是,将松散连接的大量机器转化为基于一种概念的一致系统。这些范型有的比较简单,而有的是很复杂的,但是其思想则总是提供某些东西用来统一整个系统。 分布式系统面对不同硬件和操作系统实现某种统一性的途径是,在操作系统的顶部添加一层软件。这层软件称为中间件。这层软件提供了一些特定的数据结构和操作,从而允许散布的机器上的进程和用户用一致的方式互操作。 网络硬件 以太网 经典的以太网,在 IEEE802.3 标准中有具体描述,由用来连接若干计算机的同轴电缆组成 许多计算机连接到同一根电缆上,需要一个协议来防止混乱。要在以太网上发送包,计算机首先要监听电缆,看看是否有其他的计算机正在进行传输。如果没有,这台计算机便开始传送一个包,其中有一个短包头,随后是 0 到 1500 字节的有效信息载荷 (payload)。如果电缆正在使用中,计算机只是等待直到当前的传输结束,接着该台计算机开始发送。 如果两台计算机同时开始发送,就会导致冲突发生,两台机器都做检测。采用二进制指数回退算法。 为了避免碰撞问题,现代以太网使用交换机。每个交换机有若干个端口. 一个端口用于连接一台计算机 一个以太网或另一个交换机 因特网 Internet 包括了两类计算机,主机和路由器。主机 (host) 有 PC、笔记本计算机、掌上电脑、服务器、大型计算机以及其他那些个人或公司所有且希望与 Internet 连接的计算机。 地区网络和 ISP 的路由器通过中等速度的光纤连接到主于网上。依次,每个配备路由器的公司以太网连接到地区网络的路由器上。而 ISP 的路由器则被连接到供 ISP 客户们使用的调制解调器汇集器上。按照这种方式,在 Internet 上的每台主机至少拥有通往其他主机的一条路径.而且每台经常拥有多条通往其他主机的路径。 在 Internet 上的所有通信都以包的形式传送 网络服务和协议 所有的计算机网络都为其用户(主机和进程)提供一定的服务,这种服务通过某些关干合法消息交换的规则加以实现 计算机网络为使用网络的主机和进程提供服务。 包括面向连接的服务和无连接服务。 每种服务可以用服务质量 (quality of service) 表征。有些服务就其从来不丢失数据而言是可靠的 网络协议 所有网络都有高度专门化的规则,用以说明什么消息可以发送以及如何响应这些消息 所有的现代网络都使用所谓的 协议栈 (protocol stack) 把不同的协议一层一层叠加起来。每一层解决不同的问题 大多数分布式系统都使用 Internet 作为基础,因此这些系统使用的关键协议是两种主要的 Internet 协议: IP 和 TCP. DNS 用来寻找 IP 地址 基于文档的中间件 WWW 基于文件系统的中间件 使一个分布式系统看起来像一个大型文件系统 基于对象的中间件 对象是变量的集合,这些变昼与一套称为方法的访问过程绑定在一起。进程不允许直接访间这些变量。相反,要求它们调用方法来访问。 基于协作的中间件 Linda: 耶鲁大学研发的用干通信和同步的新系统。在 Linda 系统中,相互独立的进程之间通过一个抽象的元组空间进行通信。对整个系统而言,元组空间是全局性的,在任何机器上的进程都可以把元组插入或移出元组空间,而不用考虑它们是如何存放的以及存放在何处。对于用户而言,元组空间像一个巨大的全局共享存储器 发布/订阅:它由大量通过广播网网络互联的进程组成。每个进程可以是一个信息生产者、信息消费者或两者都是。 有关多处理机系统的研究 重新设计一个专门针对多核硬件的操作系统 分布式一致性 何使大型应用可以在多核和多处理器的环境下进行扩展 调试并行应用 降低多处理器系统功耗的工作

2021 Jul 21 · 3 min

现代操作系统::虚拟化与云

虚拟化与云 介绍 在一个虚拟化系统中,不同的服务器可以运行在不同的虚拟机上,从而以更低的开销和更好的可维护性保留多计算机系统具有的局部故障模型。而且,可以在同一硬件上运行多个不同的操作系统,并享受虚拟机隔离带来的安全性和其他好处。 虚拟化技术有效的前提是绝大多数服务中断不是硬件缺陷造成的,而是由与软件设计不周、不可靠、有缺陷、配置不当造成的,特别是操作系统。 物理机数址的减少节省了硬件和电力开销以及机架空间的占用。 虚拟化技术还能在尝试新想法时提供帮助 虚拟机的另一个优势是设置检查点和虚拟机迁移(例如跨多台胀务器进行负载均衡)比在普通操作系统上运行的迁移要容易得多 在已停止支持或无法工作于当前硬件的操作系统(或操作系统版本)上运行遗留应用程序。 协助软件开发。程序员不需要在多台机器上安装不同操作系统来保证软件能在不同系统上运行。 虚拟化技术目前最重要最时髦的用途是云。云的核心思想很直接:将你的计箕或存储需求外包给一个管理良好的数据中心。 历史 20 世纪 60 年代,IBM 开发了 SIMMON 和 CP-40,运行在 System/360,重新实现成 CP-67 1974 年,加拿大的两位计算机科学家发表论文定义了一个计算机体系结构有效支持虚拟化所需满足的条件 20 世纪 90 年代,VMware 成立,后续 Xen,KVM,VirtualBox,Hyper-V,Parallels 也相继出现。 虚拟化的必要条件 虚拟机管理程序需要实现良好的表现 安全性: 虚拟机管理程序应完全掌控虚拟资源。 保真性: 程序在虚拟机上执行的行为应与在裸机上相同. 高效性: 虚拟机中运行的大部分代码应不受虚拟机管理程序的干涉。 安全性 解释器(例如 Bochs)中逐条考虑指令井准确执行其行为是一种安全执行指令的方式。 保真性 每个包含内核态和用户态的 CPU 都有一个特殊的指令集合,其中的指令在内核态和用户态执行的行为不同。这些指令包括进行 I/O 操作和修改 MMU 设置的指令,Popek 和 Goldberg 称之为敏感指令 (sensitive instruction)。 还有另一个指令集合,其中的指令在用户态执行时会导致陷人,Popek 和 Goldberg 称之为特权指令 他们的论文首次指出机器可虚拟化的一个必要条件是敏感指令为特权指令的子集 Intel 和 AMD 在 CPU 中引入虚拟化支持 VT,使问题得以解决。VM 技术的基本思想是创建可以运行虚拟机的容器。客户操作系统在容器中启动井持续运行,直到触发异常并陷入虚拟机管理程序 半虚拟化 半虚拟化提供一层类似物理机器的软件接口,显式暴露出自身是一个虚拟化的环境.例如,它提供一组虚拟化调用 (hypercall), 允许客户机向虚拟机管理程序发送显式的请求,就像系统调用为应用程序提供服务那样。客户机使用虚拟化调用执行特权操作,如修改页表等,但由于操作是客户机和虚拟机管理程序协作完成的,因此整个系统更加简单快速。 第一类和第二类虚拟机管理程序 第一类虚拟机管理程序就像一个操作系统,因为它是唯一一个运行在最高特权级的程序。 它的工作是支持真实硬件的多个虚拟机 (virtual machine) 拷贝,类似千普通操作系统支持的进程。 第二类虚拟机管理程序则不同。它是一个依赖于 Windows、 Linux 等操作系统分配和调度资源的程序,很像一个普通的进程。寄托于宿主操作系统提供大量的功能。 高效虚拟化技术 在不支持虚拟化的平台上实现虚拟化 虚拟机上的操作系统认为自己运行在内核态(实际上不是)。我们称之为虚拟内核态。 在不支持 VT 的 CPU 上,执行内核态才能执行指令会失败井导致操作系统崩溃。在支持 VT 的 CPU 上, 客户操作系统执行敏感指令时,会陷入虚拟机管理程序,虚拟机管理程序可以检查这条指令是由虚拟机中的客户操作系统执行的还是用户程序执行的。如果是前者,虚拟机管理程序将安排这条指令功能的正确执行。否则,虚拟机管理程序将换拟其实硬件面对用户态执行敏感指令时的行为。 在不支持 VT 的平台上,软件工程师们利用二进制翻译和 x86 平台确实存在的硬件特性(如处理器的特权级)构建出了虚拟机系统。 进制翻译器改写运行在第 1 级的客户操作系统,虚拟机管理程序运行在第 0 级 直接运行客户机代码井使用完全相同的技术需要第二类虚拟机管理程序能在最底层操纵硬件,这在用户态无法实现,因此,大多数现代的第二类虚拟机管理程序有一个在第 0 级运行的内核模块,能够使用特权指令操纵硬件。 虚拟化的开销 VT 硬件使用的陷入井模拟方法会产生大量陷入,而陷入在现代硬件上开销很大,因为 CPU 高速缓存、 TLB、转移预测都会受到不利影响。 使用二进制翻译后,代码既有可能变快,也有可能变慢。替换了部分需要陷入的指令。 虚拟机管理程序是真正的微内核吗 第一类和第二类虚拟机管理程序都支持未修改的客户操作系统,但需要费尽于辛万苦才能取得较好 的性能 半虚拟化 采取了不同的方法,要求修改客户操作系统的諒代码。半虚拟化的客户机执行虚拟化调用而不是敏感指令,这套调用接口实际上构成了应用编程接口 (Application Programming Interface, API), 虽然接口由客户操作系统而非应用程序使用。 移除客户操作系统中的所有敏感指令,只让它通过虚拟化调用访问 I/O 等系统服务,就将虚拟机管理程序变成了微内核 内存虚拟化 对每台虚拟机,虚拟机管理程序都需要创建一个影子页表, 将虚拟机使用的虚拟页映射到它分配给虚拟机的实际物理页上。 嵌套页表的硬件支持 使用影子页表会造成巨大的开销。 芯片生产商添加了嵌套页表 (nested page table) 的硬件支持。在无需陷入的悄况下由硬件处理虚拟化引发的额外页表操作,以降低开销。 回收内存 一个小的气球模块作为伪设备驱动程序加载到每个虚拟机中,与虚拟机管理程序通信。气球模块在虚拟机管理程序的请求下可以通过申请锁定页面来膨胀,也可以通过释放这些页面而紧缩。气球膨胀,客户机的实际可用物理内存减少,客户操作系统将以换出最不重要页面的方式响应这一变化,正如期望的那样。反过来,气球紧缩,客户机可用内存增加。虚拟机管理程序让操作系统来帮它作决定,通俗地讲这叫踢皮球。 I/O 虚拟化 I/O MMU 设备隔离 设备域 单根 I/O 虚拟化 虚拟应用 软件开发人员可以精心构造一个虚拟机.装上需要的操作系统、编译器、运行库和应用程序代码,固定整个虚拟机使之可以随时运行。这个虚拟机镜像可以刻录到 CD-ROM 或发布到网 站上让用户安装或下载。这种方法意味着只有软件开发人员需要知道所有的依赖关系。客户得到的是能 实际运行的完整包,与他们使用的操作系统和安装的其他软件包、运行库完全无关。这类"盒装”的虚 拟机通常称作虚拟应用 多核 CPU 上的虚拟机 由于多核芯片上的所有核心共享相同的 RAM, 因此单一的四核芯片根据摇要可以很容易地配置成 32 节点多处理器或多计算机系统。 授权问题 某些情况下,软件生产商会在授权中显式加入禁止在(未授权)虚拟机上运行软件的条款 云 云的特征 按需自助服务 。无需人为操作就能自动为用户提供资源。 普适的网络访问 。所有资源都可以通过网络用标准化的机制访问,以支持各种异构设备。 资源池。云提供商拥有的资源可以服务多个用户并动态再分配,用户通常不知道他们使用的资源 的具体位置。 快速可伸缩 。能根据用户需求弹性甚至是自动地获取和释放资源。 服务可计量。云提供商按服务类型计量用户使用的资源。 云即服务 将计算资源集中到少数儿个地方可以实现规模经济效益。将计算处理工作外包意味着不用再过于关心 IT 基础设施的管理、备份、维护、折旧、伸缩性、可靠性、性能甚至安全性。所有这些工作都梊中在一处完成,假设云提供商是称职的,则这些都能很好地完成。 虚拟机迁移 使用内存预复制迁移 (pre-copy memory migration) , 能在虚拟机提供服务的同时复制内存页。大多数内存页的写入井不频繁,直接复制是安全的。但是,虚拟机仍在运行,所以页面复制之后可能会被修改。页面修改时将其标记为脏的,以确保最新版本复制到目标机器。脏页面会重新复制。当大多数页面复制完成后,只剩少量脏页面。短暂地暂停虚拟机以复制它们,然后在目标机器上恢复虚拟机执行。虽然仍有暂停,但时间很短,应用程序通常不会受到影响。若停机时间不箕太耗时,就称作无缝热迁移 虚拟机与物理硬件的解耦还有其他优势,尤其是可以暂停一个虚拟机,保存快照的最直接方式是复制所有状态,包括完整的文件系统。然而,即使磁盘速度很快,复制上 TB 的磁盘内容也会花点时间。和前面的虚拟机迁移相同,我们不想暂停太久。解决方案是使用写时复制技术,数据只有在绝对必需时才进行复制。 有关虚拟化和云的研究 虚拟化技术 无线网络的虚拟化 嵌套虚拟化

2021 Jul 20 · 1 min

现代操作系统::死锁

死锁 资源 需要排他性使用的对象称为资源 硬件资源,打印机,蓝牙驱动器 一组信息,数据库中加锁的数据 可抢占资源 可以从拥有它的进程中抢占而不会产生任何副作用,存储器就是一类可抢占的资源 不可抢占资源 在不引起相关的计算失败的情况下,无法把它从占有它的进程处抢占过来 某个资源是否可抢占取决干上下文环境。在一台标准的 PC 中,内存中的页面总是可以置换到磁盘中并置换回来,故内存是可抢占的。但是在一部不支持交换和页面调度的智能机上,仅通过将内存消耗大户交换出来是不能避免死锁的。 总的来说,死锁与不可抢占资源有关,有关可抢占资源的潜在死锁通常可以通过在进程之间重新分配资源而化解。 若请求时资源不可用,则请求进程被迫等待。某些进程会自动阻塞,有一些会发挥错误代码并在稍后重试。 资源获取 对于数据库系统中的记录这类资源,应该由用户进程来管理其使用. 一种允许用户管理资源的可能方法是为每一个资源配置一个信号量。 死锁简介 如果一个进在集合中的每个进在都在等持只能由该进在集合中的其他进程才能引发的事件,那么,该进在集合就是死锁的. 资源死锁的条件,同时满足以下四个条件 互斥条件.每个资源要么已经分配给了一个进程,要么就是可用的 占有和等待条件。已经得到了某个资源的进程可以再请求新的资源。 不可抢占条件。已经分配给一个进程的资源不能强制性地被抢占,它只能披占有它的进程显式地释放。 环路等待条件。死锁发生时,系统中一定有由两个或两个以上的进程组成的一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。 死锁建模 Holt 指出如何用有向图建立上述四个条件的模型。在有向图中有两类节点:用圆形表示的进程,用方形表示的资源。从资源节点到进程节点的有向边代表该资源已被请求、授权井被进程占用。由进程节点到资源节点的有向边表明当前进程正在请求该资源,并且该进程已被阻塞,处于等待该资源的状态。 有四种处理死锁的策眳 忽略该问题。也许如果你忽略它,它也会忽略你。 检测死锁井恢复。让死锁发生,检测它们是否发生,一旦发生死锁,采取行动解决问题。 仔细对资源进行分配,动态地避免死锁。 通过破坏引起死锁的四个必要条件之一,防止死锁的产生。 鸵鸟算法 假装根本没有问题发生 死锁检测和死锁恢复 每种类型一个资源的死锁检测 构造一张资源分配图,从中检测有向环图 每种类型多个资源的死锁检测 现在我们提供一种基于矩阵的算法来检测从 p1 到 pn 这 n 个进程中的死锁。 假设资源的类型数为 m, E1 代表资源类型 1, E2 代表资源类型 2, Ei 代表资源类型 i。E 是现有资源向量 (existing resource vector) , 现在我们需要两个数组:C 代表当前分配矩阵 (current allocation matrix), R 代表请求矩阵 (request matrix)。 C 的第 i 行代表 Pi 当前所持有的每一种类型资源的资源数。所以,Cij 代表进程 i 所持有的资源 j 的数量。同理,Rij 代表 Pi 所需要的资源 j 的数量。 每个进程起初都是没有标记过的。算法开始会对进程做标记,进程被标记后就表明它们能够被执行,不会进入死锁。当算法结束时,任何没有标记的进程都是死锁进程。该算法假定了一个最坏悄形:所有的进程在退出以前都会不停地获取资源。 死锁检测算法如下: 寻找一个没有标记的进程 Pi, 对于它而言 R 矩阵的第 i 行向让小于或等于 A。 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转到第 1 步。 如果没有这样的进程,那么算法终止。 从死锁中恢复 利用抢占恢复:在不通知原进程的情况下,将某一资源从一个进程强行取走给另一个进程使用,接着又送回,这种做法是否可行主要取决于该资源本身的特性。用这种方法恢复通常比较困难或者说不太可能。若选择挂起某个进程,则在很大程度上取决干哪一个进程拥有比较容易收回的资源。 利用回滚恢复:周期性地对进程进行检查点检查,进程检查点检查查就是将进程的状态写入一个文件以备以后重启。该检查点中不仅包括存储映像,还包括了资源状态,即哪些资源分配给了该进程。一旦检测到死锁,就很容易发现需要哪些资源。为了进行恢复,要从一个较早的检查点上开始,这样拥有所需要资源的进程会回滚到一个时间点,在此时间点之前该进程获得了一些其他的资源。在该检查点后所做的所有工作都丢失。 通过杀死进程恢复:最直接也是最简单的解决死锁的方法是杀死一个或若干个进程。一种方法是杀掉环中的一个进程。如果走运的话,其他进程将可以继续。如果这样做行不通的话,就需要继续杀死别的进程直到打破死锁环。另一种方法是选一个环外的进程作为牺牲品以释放该进程的资源。在使用这种方法时,选择一个要被杀死的进程要特别小心,它应该正好持有环中某些进程所需的资源. 死锁避免 安全状态和不安全状态 单个资源的银行家算法 多个资源的银行家算法 死锁预防 破坏互斥条件 假脱机硬件资源 破坏占有并等待条件 规定所有进程在开始执行前请求所需的全部资源。如果所需的全部资源可用,那么就将它们分配给这个进程,千是该进程肯定能够运行结束。如果有一个或多个资沥正被使用,那么就不进行分配,进程等待。 破坏不可抢占条件 一些资源可以通过虚拟化的方式来避免发生这样的情况。假脱机打印机向磁盘输出,并且只允许打印机守护进程访问真正的物理打印机,这种方式可以消除涉及打印机的死锁 破坏环路等待条件 保证每一个进程在任何时刻只能占用一个资源,如果要请求另外一个资源. 它必须先释放第一个资源。 进程可以在任何时刻提出资源请求,但是所有诮求必须按照资源编号的顺序(升序)提出。 其他问题 两段式加锁: 在第一阶段,进程试图对所有所需的记录进行加锁,一次锁一个记录。如果第一阶段加锁成功,就开始第二阶段,完成更新然后释放锁。在第一阶段并没有做实际的工作。 如果在第一阶段某个进程需要的记录已经被加锁,那么该进程释放它所有加锁的记录,然后重新开始第一阶段。 通信死锁: 通信死锁不能通过对资源排序(因为没有)或者通过仔细地安排调度来避免(因为任何时刻的诮求都是不允许被延迟的)。幸运的是,另外一种技术通常可以用来中断通信死锁:超时。 活锁: 在某些情况下,当进程意识到它不能获取缓冲区路由器所需要的下一个锁时,就会尝试礼貌地释放已经获得的锁,然后等待 1ms, 再尝试一次。 饥饿: 动态运行的系统中,在任何时刻都可能请求资源。这就需要一些策略来决定在什么时候谁获得什么资源。虽然这个策略表面上很有道理,但依然有可能使一些进程永远得不到服务,虽然它们并不是死锁进程。饥饿可以通过先来先服务资源分配策略来避免。在这种机制下,等待最久的进程会是下一个被调度的进程。随着时间的推移,所有进程都会变成最“老”的,因而,最终能够获得资源而完成。 有关死锁的研究 死锁避免 死锁检测 分布式死锁检测

2021 Jul 19 · 1 min

现代操作系统::输入/输出

输入/输出 I/O 硬件原理 I/O 设备 分类 块设备,块设备把信息存储在固定大小的块中.每个块有自己的地址。通常块的大小在 512 字节至 65536 字节之间。所有传输以一个或多个完整的(连续的)块为单位。块设备的基本特征是每个块都能独立干其他块而读写。硬盘、蓝光光盘和 USB 盘是最常见的块设备。 字符设备,字符设备以字符为单位发送或接收一个字符流,而不考虑任何块结构。字符设备是不可寻址的,也没有任何寻道操作。打印机、鼠标、键盘以及大多数与磁盘不同的设备都可以看作字符设备。 设备控制器 I/O 设备一般由机械部件和电子部件两部分组成。通常可以将这两部分分开处理,以提供更加模块化和更加通用的设计。电子部件称作设备控制器 (device controller) 或适配器 (adapter)。在个人计算机上,它经常以主板上的芯片的形式出现,或者以插入 (PCI) 扩展槽中的印刷电路板的形式出现。机械部件则是设备本身。 控制器与设备之间的接口通常是一个很低层次的接口。 控制器的任务是把串行的位流转换为字节块,井进行必要的错误校正工作。 内存映射 I/O 每个控制器有几个寄存器用来与 CPU 进行通信。通过写人这些寄存器,操作系统可以命令设备发送数据接收数据、开启或关闭,或者执行某些其他操作。通过读取这些寄存器,操作系统可以了解设备的状态,是否准备好接收一个新的命令等。 除了这些控制寄存器以外,许多设备还有一个操作系统可以读写的数据缓冲区。 每个控制寄存器被分配一个 I/O 端口 (I/O port) 号,这是一个 8 位或 16 位的整数。所 有 I/O 端口形成 I/O 端口空间 (I/O port space), 井且受到保护使得普通的用户程序不能对其进行访问(只有操作系统可以访问)。CPU 通过这个 I/O 端口和控制器通信。 另一种方式是将所有控制寄存器映射到内存空间中,每个控制寄存器被分配唯一的一个内存地址,井且不会有内存被分配这一地址。这样的系统称为内存映射 I/O 直接存储器存取 CPU 从 I/O 控制器请求数据浪费 CPU 时间,所以经常用到一种称为直接存储器存取的方案。 DMA 的工作过程 CPU 通过设置 DMA 控制器的寄存器对它进行编程,所以 DMA 控制器知道将什么数据传送到什么地方。 DMA 控制器通过在总线上发出一个读请求到磁盘控制器而发起 DMA 传送 磁盘控制器从其内部缓冲区读取内容后,写到内存中。 当写操作完成时,磁盘控制器在总线上发出一个应答信号到 DMA 控制器 DMA 控制器步增要使用的内存地址,井且步减字节计数。如果字节计数仍然大于 O, 则重复第 2 步到第 4 步,直到字节计数到达 0。此时,DMA 控制器将中断 CPU 以便让 CPU 知道传送现在已经完成了。当操作系统开始工作时,用不着将磁盘块复制到内存中,因为它已经在内存中了。 总线操作模式 字模式:DMA 控制器请求传送一个字并且得到这个字。如果 CPU 也想使用总线,它必须等待。这一机制称为周期窃取(cycle stealing),因为设备控制器偶尔偷偷溜入并且从 CPU 偷走一个临时的总线周期,从而轻微地延迟 CPU。 块模式:在块模式中,DMA 控制器通知设备获得总线,发起一连串的传送,然后释放总线。这一操作形式称为突发模式 (burst mode)。它比周期窃取效率更高,因为获得总线占用了时间,井且以一次总线获得的代价能够传送多个字。突发模式的缺点是,如果正在进行的是长时间突发传送,有可能将 CPU 和其他设备阻塞相当长的周期。 飞跃模式:DMA 控制器通知设备控制器直接将数据传送到主存。某些 DMA 控制器使用的其他校式是让设备控制器将字发送给 DMA 控制器. DMA 控制器然后发起第 2 个总线请求将该字写到它应该去的任何地方。采用这种方案,每传送一个字需要一个额外的总线周期,但是更加灵活,因为它可以执行设备到设备的复制甚至是内存到内存的复制。 重温中断 在硬件层面,中断的工作如下所述。当一个 I/O 设备完成交给它的工作时,它就产生一个中断(假设操作系统已经开放中断),它是通过在分配给它的一条总线信号线上置起信号而产生中断的。该信号被主板上的中断控制器芯片检测到,由中断控制器芯片决定做什么。 中断信号导致 CPU 停止当前正在做的工作井且开始做其他的事情。地址线上的数字被用做指向一个称为中断向量 (interrupt vector) 的表格的索引,以便读取一个新的程序计数器。这一程序计数器指向相应的中断服务过程的开始. 中断服务过程开始运行后,它立刻通过将一个确定的值写到中断控制器的某个 I/O 端口来对中断做出应答。这一应答告诉中断控制器可以自由地发出另一个中断。通过让 CPU 延迟这一应答直到它准备好处理下一个中断,就可以避免与多个几乎同时发生的中断相牵涉的竞争状态。 在开始服务程序前,硬件需要保存必要的信息。 精确中断和不精确中断 将机器留在一个明确状态的中断称为精确中断 PC(程序计数器)保存在一个已知的地方 。 PC 所指向的指令之前的所有指令已经完全执行。 PC 所指向的指令之后的所有指令都没有执行。 PC 所指向的指令的执行状态是已知的。 不满足以上条件的中断称为不精确中断 具有不精确中断的机器通常将大量的内部状态"吐出”到堆栈中,从而使操作系统有可能判断出正在发生什么事情。重新启动机器所必需的代码通常极其复杂。此外,在每次中断发生时将大让的信息保存在内存中使得中断响应十分缓慢,而恢复则更加糟糕。 某些超标量计算机(例如 x86 系列)具有精确中断,从而使老的软件正确工作。为与精确中断保持后向兼容付出的代价是 CPU 内部极其复杂的中断逻辑,以便确保当中断控制器发出信号想要导致一个中断时,允许直到某一点之前的所有指令完成而不允许这一点之后的指令对机器状态产生任何重要的影响。 此处付出的代价不是在时间上,而是在芯片面积和设计复杂性上。如果不是因为向后兼容的目的而要求精确中断的话,这一芯片面积就可以用于更大的片上高速缓存,从而使 CPU 的速度更快。另一方面,不精确中断使得操作系统更为复杂而且运行得更加缓慢,所以断定哪一种方法更好是十分困难的. I/O 软件原理 I/O 软件的目标 在设计 I/O 软件时一个关键的概念是设备独立性 (device independence)。它的意思是应该能够编写出这样的程序:它可以访问任意 I/O 设备而无需事先指定设备。 与设备独立性密切相关的是统一命名 (uniform naming) 这一目标。一个文件或一个设备的名字应该是一个简单的字符串或一个整数,它不应依赖于设备。 I/O 软件的另一个重要问题是错误处理 (error handling)。一般来说,错误应该尽可能地在接近硬件的层面得到处理。只有在低层软件处理不了的情况下,才将错误上交高层处理。在许多情况下,错误恢复可以在低层透明地得到解决,而高层软件甚至不知道存在这一错误。 同步 (synchronous 即阻塞)和异步 (asynchronous, 即中断驱动)传输。大多数物理 I/0 是异步的,CPU 启动传输后便转去做其他工作,直到中断发生。如果 I/O 操作是阻塞的,那么用户程序就更加容易编写一在 read 系统调用之后,程序将自动被挂起,直到缓冲区中的数据准备好。正是操作系统使实际上是中断驱动的操作变为在用户程序看来是阻塞式的操作。然而,某些性能极高的 应用程序需要控制 I/O 的所有细节,所以某些操作系统使异步 I/O 对这样的应用程序是可用的。 缓冲 (buffering)。数据离开一个设备之后通常并不能直接存放到其最终的目的地,而是放到了某个地方对它进行检查,才能确定最后需要发送到哪里,缓冲区填满和清空的速率也影响 I/O 性能。 设备独占和共享问题。打印机之类的只允许一个用户使用,某些文件被多人打开同时写入。 程序控制 I/O 考虑一个用户进程,该进程想通过串行接口在打印机 上打印 8 个字符的字符串"ABCDEFGH"。 软件首先要在用户空间的一个缓冲区中组装字符串 用户进程通过发出打开打印机一类的系统调用来获得打印机以便进行写操作 操作系统(通常)将字符串缓冲区复制到内核空间中的一个数组(如 p)中 操作系统要查看打印机当前是否可用。如果不可用,就要等待直到它可用 操作系统就复制第一个字符到打印机的数据寄存器中,在这个例子中使用了内存映射 I/O 这时,操作系统将等待打印机状态再次变为就绪。打印机就绪事件发生时,操作系统就打印下一个字符,直到过程完成,返回用户进程。 中断驱动 I/O 在不缓冲字符而是在每个字符到来时便打印的打印机上进行打印的情形。 当程序把字符写入打印机的寄存器后,在打印机就绪前切换上下文去执行别的程序。 当打印机就绪的时候发出一个中断,程序被调度回来继续执行直到完成。 使用 DMA 的 I/O 让 DMA 控制器而不是 CPU 完成任务,中断次数每字节一次变成没缓冲区一次。 I/O 软件层次 中断处理程序 虽然程序控制 I/O 偶尔是有益的,但是对于大多数 I/O 而言,中断是令人不愉快的事情并且无法避免。应当将其深深隐藏在操作系统内部,一边系统的其他部分尽量不与它发生联系。隐藏它们的最好办法是将启动一个 I/O 操作的驱动程序阻塞起来,直到 I/O 操作完成并且产生一个中断。 当中断发生时,中断处理程序将做它必须要做的全部工作以便对中断进行处理。 中断最终的结果是使先前被阻塞的驱动程序现在能够继续运行。 处理过程 保存没有被中断硬件保存的所有寄存器(包括 PSW)。 为中断服务过程设置上下文,可能包括设置 TLB、 MMU 和页表。 为中断服务过程设置堆栈. 应答中断控制器,如果不存在集中的中断控制器,则再次开放中断。 将寄存器从它们被保存的地方(可能是某个堆栈)复制到进程表中。 运行中断服务过程,从发出中断的设备控制器的寄存器中提取信息. 选择下一次运行哪个进程,如果中断导致某个被阻塞的高优先级进程变为就绪,则可能选择它现在就运行。 为下一次要运行的进程设置 MMU 上下文,也许还蒂要设置某个 TLB。 装入新进程的寄存器,包括其 PSW。 开始运行新进程。 设备驱动程序 每一个控制器都设有某些设备寄存器用来向设备发出命令,或者设有某些设备寄存器用来读出设备的状态,或者设有这两种设备寄存器,寄存器的数量根据功能不同在不同的设备之间数量也不同。 每个连接到计算机上的 I/O 设备都需要某些设备特定的代码来对其进行控制。这样的代码称为设备驱动程序 (device driver), 它一般由设备的制造商编写并随同设备一起交付。 每个设备驱动程序通常处理一种类型的设备,或者至多处理一类紧密相关的设备。 不过在有些时候,极其不同的设备却基于相同的底层技术。众所周知的例子可能是 USB。 驱动程序必须是重入的 (reentrant), 这意味着一个正在运行的驱动程序必须预料到在第一次调用完成之前第二次被调用。 在一个可热插拔的系统中,设备可以在计算机运行时添加或删除。 驱动程序不允许进行系统调用,但是它们经常需要与内核的其余部分进行交互。对某些内核过程的调用通常是允许的。 与设备无关的 I/O 软件 设备驱动程序的统一接口 操作系统定义一组驱动程序必须支持的函数。 设备无关的软件要负责把符号化的设备名映射到适当的驱动程序上。 所有设备都具有主设备号和次设备号,并且所有驱动程序都是通过使用主设备号来选择驱动程序而得到访问。 在 UNIX 和 Windows 中.设备是作为命名对象出现在文件系统中的,这意味着针对文件的常规的保护规则也适用千 I/O 设备。系统管理员可以为每一个设备设置适当的访问权限。 缓冲 实现方案 用户进程在用户空间中提供了一个包含 n 个字符的缓冲区,并且执行读入 n 个字符的读操作。中断服务过程负责将到来的字符放入该缓冲区中直到缓冲区填满,然后唤醒用户进程。 在内核空间中创建一个缓冲区并且让中断处理程序将字符放到这个缓冲区中。 结构 双缓冲,当一个缓冲区满的时候使用另一个缓冲区 环形缓冲区,它由一个内存区域和两个指针组成。一个指针指向下一个空闲的字,新的数据可以放置到此处。另一个指针指向缓冲区中数据的第一个字,该字尚未被取走。 错误报告 错误在 I/O 上下文中比在其他上下文中要常见得多。当错误发生时,操作系统必须尽最大努力对它们进行处理。许多错误是设备特定的并且必须由适当的驱动程序来处理,但是错误处理的框架是设备无关的. 一种类型的 I/O 错误是编程错误,解决方案是将错误码返回给调用者。 另一种类型的错误是实际的 I/O 错误,如磁盘损坏,在这些情形中,应该由驱动程序决定做什么。如果驱动程序不知道做什么,它应该将问题向上传递,返回给与设备无关的软件。 分配与释放专用设备 某些设备,例如打印机,在任意给定的时刻只能由一个进程使用。这就要求操作系统对设备使用的诮求进行检查,并且根据被访求的设备是否可用来接受或者拒绝这些请求。 与设备无关的块大小 应该由与设备无关的软件来隐藏这一事实井且向高层提供一个统一的块大小 用户空间的 I/O 软件 大多数 I/O 事件都发生在内核,但还是有小部分发生在用户空间 假脱机目录,脱离操作系统的 I/O 事件,将需要发送的块放到文件目录中,稍后由守护进程发送出去 盘 盘硬件 磁盘 磁盘被组织成柱面,每一个柱面包含若干磁道,磁道数与垂直堆叠的磁头个数相同。磁道又被分成若干扇区,软盘上大约每条磁道有 8~32 个扇区,硬盘上每条磁道上扇区的数目可以多达几百个。磁头 数大约是 1 ~ 16 个。 磁盘驱动器本身包含一个微控制器,该微控制器承担了大员的工作并且允许实际的控制器发出一组高级命令。控制器经常做磁道高速缓存、坏块重映射以及更多的工作。 对磁盘驱动程序有重要意义的一个设备特性是:控制器是否可以同时控制两个或多个驱动器进行寻道,这就是重叠寻道 (overlapped seek)。当控制器和软件等待一个驱动器完成寻道时,控制器可以同时启动另一个驱动器进行寻道。许多控制器也可以在一个驱动器上进行读写操作,与此同时再对另一个或多个其他驱动器进行寻道,但是软盘控制器不能在两个驱动器上同时进行读写操作。 所有现代磁盘现在都支持一种称为逻辑块寻址,而不管磁盘的几何规格如何 RAID RAID 定义为 Redundant Array of Inexpensive Disk (廉价磁盘冗余阵列),但是工业界将 I 重定义为 Independent (独立)而不是 Inexpensive (廉价) RAID 背后的基本思想是将一个装满了磁盘的盒子安装到计算机(通常是一个大型服务器)上,用 RAID 控制器替换磁盘控制器卡,将数据复制到整个 RAID 上,然后继续常规的操作。 所有的 RAID 都具有同样的特性,那就是将数据分布在全部驱动器上,这样就可以并行操作。 磁盘格式化 一个扇区由前导码,数据,ECC 组成。 前导码以一定的位模式开始,位模式使硬件得以识别扇区的开始。前导码还包含柱面与扇区号以及某些其他信息。 数据部分的大小是由低级格式化程序决定的,大多数磁盘使用 512 字节的扇区。 ECC 域包含冗余信息,可以用来恢复读错误 低级格式化的结果是磁盘容量减少,减少的摄取决于前导码、扇区间间隙和 ECC 的大小以及保留的备用扇区的数目。通常格式化的容量比未格式化的容量低 20%。备用扇区不计人格式化的容量,所以一种给定类型的所有磁盘在出厂时具有完全相同的容址,与它们实际具有多少坏扇区无关 在低级格式化完成之后,要对磁盘进行分区。在逻辑上,每个分区就像是一个独立的磁盘.分区对于实现多个操作系统共存是必要的。 磁盘臂调度算法 影响读写磁盘块时间的因素 寻道时间 旋转延迟 实际数据传输延迟 调度算法 先来先服务:磁盘驱动程序每次接收一个诘求井按照接收顺序完成请求 最短寻道优先:总是处理与磁头距离最近的请求以使寻道时间最小化 电梯算法:电梯保持按一个方向移动,直到在那个方向上没有请求为止,然后改变方向 缓存 许多磁盘控制器总是读出多个扇区并对其进行高速缓存 高速缓存的使用是由控制器动态地决定的。在最简单的模式下,高速缓存被分成两个区段,一个用于读,一个用于写 磁盘控制器的高速缓存完全独立千操作系统的高速缓存 错误处理 对于坏块存在两种一般的处理方法:在控制器中对它们进行处理或者在操作系统中对它们进行处理。在前一种方法中,磁盘在从工厂出厂之前要进行测试,井且将一个坏扇区列表写在磁盘上。对千每一个坏扇区,用一个备用扇区替换它 。 有两种方法进行这样的替换 控制器所能够做的事情是将备用扇区之一重映射为坏扇区 另一种方法是将所有扇区向上移动一个扇区 在这两种情况下,控制器都必须知道哪个扇区是哪个扇区。它可以通过内部的表来跟踪这一信息(每个磁道一张表),或者通过重写前导码来给出重映射的扇区号。 第二种操作系统必须首先获得一个坏扇区列表,或者是通过从磁盘中读出该列表,或者只是由它自己测试整个磁盘。一旦操作系统知道哪些扇区是坏的,它就可以建立项映射表。 除了磁盘坏块还有寻道错误,大多数磁盘可以自动回复,通过把磁臂往外移动,并把当前柱面重置为 0 稳定存储器 一个磁盘子系统具有如下特性:当 一个写命令发给它时,磁盘要么正确地写数据,要么什么也不做,让现有的数据完整无缺地留下。 稳定存储器使用一对完全相同的磁盘,对应的块一同工作以形成一个无差错的块。当不存在错误时, 在两个驱动器上对应的块是相同的,读取任意一个都可以得到相同的结果。 稳定写 (stable write)。稳定写首先将块写到驱动器 1 上,然后将其读回以校验写的是正确的。如果写的不正确,那么就再次做写和重读操作,一直到 n 次,直到正常为止。经过 n 次连续的失败之后,就将该块重映射到一个备用块上,并且重复写和重读操作直到成功为止,无论要尝试多少个备用块。在对驱动器 1 的写成功之后,对驱动器 2 上对应的块进行写和重读,如果需要的话就重复这样的操作,直到最后成功为止。如果不存在 CPU 崩溃,那么当稳定写完成后,块就正确地被写到两个驱动器上,井且在两个驱动器上得到校验。 稳定读 (stable read)。稳定读首先从驱动器 1 上读取块。如果这一橾作产生错误的 ECC, 则再次尝试读操作,一直到 n 次。如果所有这些操作都给出错误的 ECC, 则从驱动器 2 上读取对应的数据块。给定一个成功的稳定写为数据块留下两个可靠的副本这样的事实,并且我们假设在合理的时间间隔内相同的块在两个驱动器上自发地变坏的概率可以忽略不计,那么稳定读就总是成功的 。 崩溃恢复 (crash recovery)。崩溃之后,恢复程序扫描两个磁盘,比较对应的块。如果一对块都是好的井且是相同的,就什么都不做。 如果其中一个具有 ECC 错误,那么坏块就用对应的好块来覆盖. 如果一对块都是好的但是不相同,那么就将驱动器 1 上的块写到驱动器 2 上。 时钟 时钟硬件 时钟由三个部件构成: 晶体振荡器、计数器和存储寄存器 当把一块石英晶体适当地切割并且安装在一定的电压之下时,它就可以产生非常精确的周期性信号,典型的频率范围是几百兆赫兹,具体的频率值与所选的品体有关。 在任何一台计算机里通常都可以找到至少一个这样的电路,它给计算机的各种电路提供同步信号。该信号被送到计数器,使其递减计数至 0。当计数器变为 0 时,产生一个 CPU 中断。 可编程时钟通常具有几种操作模式 一次完成模式 当时钟启动时,它把存储寄存器的值复制到计数器中,然后,来自晶体的每一个脉冲使计数器减 1。当计数器变为 0 时,产生一个中断,井停止工作,直到软件再一次显式地启动它 方波模式 当计数器变为 0 井且产生中断之后,存储寄存器的值自动复制到计数器中,并且整个过程无限期地再次重复下去。这些周期性的中断称为时钟滴答 可编程时钟芯片通常包含多个独立的可编程时钟 时钟软件 时钟硬件所做的全部工作是根据已知的时间间隔产生中断。涉及时间的其他所有工作都必须由软件时钟驱动程序完成. 维护日时间 用 64 位寄存器存滴答数 以秒为单位维护时间 以秒为单位的系统引导时间 防止进程超时运行 每当启动一个进程时,调度程序就将一个计数器初始化为 1,以时钟滴答为单位的该进程时间片的取值。每次时钟中断时,时钟驱动程序将时间片计数器减 1。当计数器变为 0 时,时钟驱动程序调用调度程序以激活另一个进程。 对 CPU 的使用情况记账 启动一个不同于主系统定时器的辅助定时器。当进程终止时,读出这个定时器的值就可以知道该进程运行了多长时间 处理用户进程提出的 alarm 系统调用 进程可以请求操作系统在一定的时间间隔之后向它报警。警报通常是信号、中断、消息或者类似的东西。需要这类报警的一个应用是网络,当一个数据包在一定时间间隔之内没有被确认时,该数据包必须重发。 为系统本身的各个部分提供监视定时器 操作系统的组成部分也摇要设置定时器,这些定时器被称为监视定时器,并且经常用来检测死机之类的问题 完成概要剖析、监视和统计信息收集 某些操作系统提供了一种机制,通过该机制用户程序可以让系统构造它的程序计数器的一个直方图,这样它就可以了解时间花在了什么地方。 软定时器 无论何时当内核因某种其他原因在运行时,在它返回到用户态之前,它都要检查实时时钟以了解软定时器是否到期。如果这个定时器已经到期,则执行被调度的事件。如,传送数据包或者桧查到来的数据包,而无需切换到内核态,因为系统已经在内核态。在完成工作之后,软定时器被复位以便再次闹响。要做的全部工作是将当前时钟值复制给定时器并且将超时间隔加上。 用户界面:键盘、鼠标和监视器 输入软件 键盘软件 硬件所做的全部工作是给出键被按下和释放的中断,其他的事情由软件来做。 例如,当 A 键被按下时,扫描码(30)被写入一个 I/O 寄存器。驱动程序应该负责确定键入的是小写字母.大写字母、 CTRL-A、ALT-A、CTRL-ALT-A 还是某些其他组合。由于驱动程序以断定哪些键已经按下但是还没有被释放(例如 SHIFR), 所以它拥有足够多的信息来做这一工作。 鼠标 当鼠标在随便哪个方向移动了一个确定的最小距离,或者按钮被按下或释放时,都会有一条消息发送给计算机。 输出软件 文本窗口 ASNI 标准,转移序列标准化,标准化移动光标等控制符的方式。 X 窗口系统 几乎所有 UNIX 系统的用户界面都以 X 窗口系统 (X Window System) 为基础, 当 X 在一台机器上运行时,从键盘或鼠标采集输入并且将输出写到屏幕上的软件称为 X 服务器 (X server)。它必须跟踪当前选择了哪个窗口(鼠标指针所在处).这样它就知道将新的键盘输入发送给哪个客户。它与称为 X 客户 (X client) 的运行中的程序进行通信 (可能通过网络 )。它将键盘与鼠标输入发送给 X 客户,井且从 X 客户接收显示命令。 X 只是一个窗口系统,它不是一个完全的 GUI。为了获得完全的 GUI, 要在其上运行共他软件层。 图形用户界面 GUI 软件可以在用户级代码中实现(如 UNIX 系统所做的那样),也可以在操作系统中实现(如 Windows 的情况 ). GUI 系统的输入仍然使用键盘和鼠标,但是输出几乎总是送往特殊的硬件电路板,称为 图形适配器(graphics adapter)。图形适配器包含特殊的内存,称为视频 RAM(video RAM),它保存出现在屏幕上的图像。 位图 GDI 过程是矢量图形学的实例.它们用于在屏幕上放置几何图形和文本。 每一个网格方块的平均红、绿、蓝取值被采样井且保存为一个像素的值。这样的文件称为位图 字体 TrueType 的引入解决了字体放大缩小都可以完整显示的问题。 触摸屏 电阻屏,ITO 电荷移动获取位置 电容屏,有两层表面,手指触摸改变两层之间的电容。 瘦客户机 大多数用户想要高性能的交互式计算,但是实在不想管理一台计算机。这一结论导致研究人员重新研究了分时系统使用的哑终端 电源管理 硬件问题 电池一般分为两种类型: 一次性使用的和可再充电的。 通过电源按键休眠唤醒计算机 如何在休眠时候降低功耗 操作系统问题 显示器,休眠唤醒,降低亮度 硬盘,在一定时间不读取时休眠,重新启动磁盘比较耗时,通过高速缓存降低延迟 CPU,cpu 降低电压和频率 内存,关闭高速缓存,将内容写到磁盘上然后关闭主存本身 无线通信,基站缓存消息,通行设备休眠并且不错过消息 热量管理,监控温度,在温度达到临界值时启动风扇 电池管理,当电量快耗尽提醒用户 驱动程序接口,操作系统可以发命令给驱动程序通知它们消减功耗 应用程序问题 降低视频帧率,减少像素 降低语音识别器的功耗,使用比较小的词汇量和简单的声学模型 裁剪传输的内容,减少传输功耗 选择合适的 JPEG 算法 有关输入/输出的研究 改善输入/输出的底层结构 改善特定设备的表现 网络,服务质量和性能 减少能耗 时钟 中断延迟 设备驱动 瘦客户端

2021 Jul 18 · 3 min

现代操作系统::文件系统

文件系统 文件 文件命名 文件是一种抽象机制,它提供了一种在磁盘上保存信息而且方便以后读取的方法。这种方法可以使用户不必了解存储信息的方法、位置和实际磁盘工作方式等有关细节。 文件的具体命名规则在各个系统中是不同的,不过所有的现代操作系统都允许用 1 至 8 个字母组成的字符串作为合法的文件名。 有些文件系统区分大小写字母,有些则不区分。 UNIX 属于前一类,老的文件系统 MS-DOS 则属于后一类。 许多操作系统支持文件名用圆点隔开分为两部分,如文件名 prog.c。圆点后面的部分称为 文件扩展 名 (file extension) 在某些系统中(如所有 UNIX 版本),文件扩展名只是一种约定,操作系统井不强迫采用它。Windows 关注扩展名且对其赋予了含义。 文件结构 字节序列,一种无结构的字节序列,只对特定程序有意义,目前主流操作系统采取的方案。 记录序列,文件是具有固定长度记录的序列,每个记录都有其内部结构。把文件作为记录序列的中心思想是: 读操作返回一个记录,而写操作重写或追加一个记录。穿孔卡片采用的方案。 树,这种结构中由一棵记录树构成,每个记录不必具有相同的长度,记录的固定位置上有一个键字段。可以根据特定字段找到记录的位置,在一些处理商业数据的服务器上采用。 文件类型 普通文件,含有包含用户信息的文件。 ASCII 文件,有多行正文组成,可以显示打印和用编辑器编辑。 二进制文件,有特定结构,只对使用该结构的程序有意义。 目录,管理文件系统结构的系统文件。 字符特殊文件,与输入/输出有关,用于串行 I/O 类设备,如终端、打印机、网络等 块特殊文件用于磁盘类设备 文件访问 顺序访问,从头按顺序读取文件的全部字节或记录,但不能跳过某一些内容,也不能不按顺序读取。 随机访问文件,能够以任何次序读取其中字节或记录。 文件属性 名字 权限 创建时间 加锁标记 等等 文件操作 create。创建不包含任何数据的文件。该调用的目的是表明文件即将建立,井设置文件的一些属性。 delete。当不再需要某个文件时,必须删除该文件以释放磁盘空间。任何文件系统总有一个系统调用用来删除文件。 open。在使用文件之前,必须先打开文件。open 调用的目的是:把文件属性和磁盘地址表装入内存,便于后续调用的快速访问。 close。访问结束后,不再需要文件属性和磁盘地址,这时应该关闭文件以释放内部表空间。很多系统限制进程打开文件的个数,以鼓励用户关闭不再使用的文件。磁盘以块为单位写入,关闭文件时,写入该文件的最后一块,即使这个块还没有满。 read。在文件中读取数据。 一般地,读取的数据来自文件的当前位置。调用者必须指明需要读取多少数据,并且提供存放这些数据的缓冲区。 write。向文件写数据,写操作一般也是从文件当前位置开始。如果当前位置是文件末尾,文件长度增加。如果当前位置在文件中间,则现有数据被覆盖,并且永远丢失。 append。此调用是 write 的限制形式,它只能在文件末尾添加数据。若系统只提供最小系统调用集合,则通常没有 append。很多系统对同一操作提供了多种实现方法,这些系统中有时有 append 调用. seek。对干随机访问文件,要指定从何处开始获取数据,通常的方法是用 seek 系统调用把当前位置指针指向文件中特定位置。 seek 调用结束后,就可以从该位置开始读写数据了。 get attributes。进程运行常需要读取文件属性。例如, UNIX 中 make 程序通常用于管理由多个源文件组成的软件开发项目。在调用 make 时,它会检查全部源文件和目标文件的修改时间,实现最小编译,使得全部文件都为最新版本。为达到此目的,需要查找文件的某一些属性,即修改时间。 set attributes。某些属性是可由用户设置的,甚至是在文件创建之后,实现该功能的是 set attributes 系统调用。保护模式信息是一个典型的例子,大多数标志也属于此类属性。 rename。用户常常要改变已有文件的名字, rename 系统调用用于这一目的。严格地说, rename 系统调用不是必需的,因为先把文件复制到一个新文件中,然后删除原来的文件,就可以达到同样的目的。 目录 一级目录系统 目录系统的最简单形式是在一个目录中包含所有的文件。这有时称为根目录 层次目录系统 用户可以创建任意数量的子目录,每个目录又可以继续创建子目录。 路径名 绝对路径名,路径名从根目录开始,不同路径之间用’/‘分割 相对路径名,从当前工作目录开始,‘.’ 表示当前路径,‘..‘表示父目录。 目录操作 create。创建目录。除了目录项".“和” ..“外,目录内容为空。目录项”.“和 “..“是系统自动放在目录中的(有时通过 mkdir 程序完成) 。 delete。 删除目录。只有空目录可删除。只包含目录项”.“和”..“的目录被认为是空目录,这两个目录项通常不能删除。 opendir。目录内容可被读取。例如,为列出目录中全部文件,程序必须先打开该目录,然后读其中全部文件的文件名。与打开和读文件相同,在读目录前,必须打开目录。 closedir。读目录结束后,应关闭目录以释放内部表空间。 readdir。系统调用 readdir 返回打开目录的下一个目录项。以前也采用 read 系统调用来读目录,但这方法有一个缺点: 程序员必须了解和处理目录的内部结构。相反,不论采用哪一种目录结构,readdir 总是以标准格式返回一个目录项。 rename。在很多方面目录和文件都相似。文件可换名,目录也可以 。 link。链接技术允许在多个目录中出现同一个文件。这个系统调用指定一个存在的文件和一个路径名,并建立从该文件到路径所指名字的链接。这样,可以在多个目录中出现同一个文件。这种类型的链接增加了该文件的 i 节点 (i-node) 计数器的计数(记录含有该文件的目录项数目),有时称为硬链接(hard link)。 unlink。删除目录项。如果被解除连接的文件只出现在一个目录中(通常情况) ,则将它从文件系统中删除。如果它出现在多个目录中,则只删除指定路径名的连接,依然保留其他路径名的连接。在 UNIX 中,用于删除文件的系统调用(前面已有论述)实际上就是 unlink. 符号链接 不同于使用两个文件名指向同一个内部数据结构来代表一个文件 符号链接的优点在于它能够跨越磁盘的界限 文件系统的实现 文件系统布局 文件系统存放在磁盘上。多数磁盘划分为一个或多个分区,每个分区中有一个独立的文件系统 磁盘的 0 号扇区称为主引导记录,用来引导计算机,MBR 后面是分区表 一个可能的文件系统布局包括:引导块、超级块、空闲空间管理、i 节点、根目录、文件和目录 文件的实现 连续分配:最简单的分配方案是把每个文件作为一连串连续数据块存储在磁盘上,实现简单,读操作性能好,有磁盘碎片问题。 链表分配:存储文件的第二种方法是为每个文件构造磁盘块链表。每个块的第一个字作为指向下一块的指针,块的其他部分存放数据。可以充分利用磁盘块,顺序访问块,随机访问慢,需要从头开始遍历。因为指针占据了部分空间,导致发生额外的拼接和复制。 使用内存中的表进行链表分配 如果取出每个磁盘块的指针字,把它们放在内存的一个表中,就可以解决上述链表的两个不足。内存中存在这样一个表格叫做 FAT。仍然需要顺着链表查找给定的偏移量。缺点是需要把整张表存在内存中。 i 节点 每个文件赋予一个称为 i 节点的数据结构,其中列出了文件属性和文件块的磁盘地址。对比 FAT 的优势,只有在打开该文件时才加载该数据结构。 目录的实现 目录中有一个固定大小的目录项列表,每个文件对应一项,其中包含一个(固定长度) 文件名、一个文件属性的结构体以及用以说明磁盘块位置的一个或多个磁盘地址(至某个最大值)。 对于采用 i 节点的系统,把文件属性存放在 i 节点中而不是目录项中。 采用散列表可以加速查找文件名,另一种方式是采用高速缓存。 共享文件 当几个用户同在一个项目里工作时,他们常常需要共享文件。 如果 C 的一个文件现在也出现在 B 的目录下。 B 的目录与该共享文件的联系称为一个链接 (link)。 共享的实现 磁盘块不列入目录,而是列入一个与文件本身关联的小型数据结构中 。目录将指向这个小型数据结构。这是 UNIX 系统中 所采用的方法 通过让系统建立一个类型为 LINK 的新文件,井把该文件放在 B 的目录下,使得 B 与 C 的一个文件存在链接。新的文件中只包含了它所链接的文件的路径名。当 B 读该链接文件时,操作系统查看到要读的文件是 LINK 类型,则找到该文件所链接的文件的名字,并且去读那个文件。与传统(硬)链接相对比起来,这一方法称为符号链接 日志结构文件系统 将整个磁盘结构化为一个日志,每隔一段时间,或是有特殊需要时,被缓冲在内存中的所有未决的写操作都被放到一个单独的段中,作为在日志末尾的一个邻接段写入磁盘。这个单独的段可能会包括 i 节点、目录块、数据块或者都有。 每一个段的开始都是该段的摘要,说明该段中都包含哪些内容。 i 节点分布在日志中,查找比较困难,需要维护额外的信息帮助查找 i 节点。 总而言之,所有的写操作最初都被缓冲在内存中,然后周期性地把所有已缓冲的写作为一个单独的段,在日志的末尾处写入磁盘 .要打开一个文件,则首先需要从 i 节点图中找到文件的 i 节点 。一旦 i 节点定位之后就可以找到相应的块的地址。所有的块都放在段中,在日志的某个位置上。 LFS 有一个清理线程,该清理线程周期地扫描日志进行磁盘压缩。整个磁盘抽象成一个大的环形缓冲区。LFS 处理大量零碎的读写性能比 UNIX 好一个数量级,在读写大块的性能方面也不逊色。 日志文件系统 对于需要几个步骤完成的操作。 日志文件系统则先写一个日志项,列出几个将要完成的动作。然后日志项被写人磁盘( 并且为了良好地实施,可能从磁盘读回来验证它的完整性)。只有当日志项已经被写人,不同的操作才可以进行。 当所有的操作成功完成后,擦除日志项。如果系统这时崩溃.系统恢复后,文件系统可以通过检查日志来查看是不是有未完成的操作。如果有,可以煎新运行所有未完成的操作直到文件被正确地删除。直到操作完成。 日志的写入需要幂等 文件系统可以引入原子事务的概念。 虚拟文件系统 尝试将多种文件系统统一成一个有序的结构。关键的思想就是抽象出所有文件系统都共有的部分,并且将这部分代码放在单独 的一层,该层调用底层的实际文件系统来具体管理数据。 所有和文件相关的系统调用在最初的处理上都指向虚拟文件系统。这些来自用户进程的调用,都是标准的 POSIX 系统调用,比如 open、 read、 write 和 lseek 等。因此,VFS 对用户进程有 一 个“上层“接口,它就是著名的 POSIX 接口。 文件系统管理和优化 磁盘空间管理 存储一个有 n 个字节的文件可以有两种策略:分配 n 个字节的连续磁盘空间,或者把文件分成很多个连续(或井不一定连续) 的块。 几乎所有的文件系统都把文件分割成固定大小的块来存储,各块之间不一定相邻。 块大小 需要从传输速度和磁盘利用率综合考虑 记录空闲块 磁盘块链表,链表的每个块中包含尽可能多的空闲磁盘块号。 位图,空闲块用 1 表示,已分配块用 0 表示(或者反之)。 磁盘配额 多用户操作系统常常提供一种强制性磁盘配额机制。其思想是系统管理员分给每个用户拥有文件和块的最大数量、操作系统确保每个用户不超过分给他们的配额。 当用户打开一个文件时,系统找到文件属性和磁盘地址,井把它们送入内存中的打开文件表。其中一个属性告诉文件所有者是谁。任何有关该文件大小的增长都记到所有者的配额上,以防止一个用户垄断所有 i 节点。 文件系统备份 增量转储,周期性地(每周一次或每月一次)做全面的转储(备份),而每天只对从上一次全面转储起发生变化的数据做备份。 物理转储,磁盘的第 0 块开始,将全部的磁盘块按序输出到磁带上,直到最后一块复制完毕。 逻辑转储,从一个或几个指定的目录开始,递归地转储其自给定基准日期(例如,最近一次增量转储或全面系统转储的日期) 后有所更改的全部文件和目录。所以,在逻辑转储中,转储磁带上会有一连串精心标识的目录和文件,这样就很容易满足恢复特定文件或目录的请求。 文件系统的一致性 块的一致性检查 文件的一致性检查 文件系统性能 高速缓存,检查全部的读请求,查看在高速缓存中是否有所需要的块。如果存在,可执行读操作而无须访问磁盘。如果该块不在高速缓存中,首先要把它读到高速缓存,再复制到所需地方。之后,对同一个块的请求都通过高速缓存完成。 块提前读,在需要用到块之前,试图提前将其写入高速缓存,从而提高命中率 减少磁盘臂运动,把有可能顺序访问的块放在一起,当然最好是在同一个柱面上,从而减少磁盘臂的移动次数。 磁盘碎片整理 移动文件使它们相邻,并把所有的(至少是大部分的)空闲空间放在一个或多个大的连续的区域内。 固态硬盘并不受磁盘碎片的影响。事实上,在固态硬盘上做磁盘碎片整理反倒是多此一举,不仅没有提高性能,反而磨损了固态硬盘。所以碎片整理只会缩短固态硬盘的寿命 。 有关文件系统的研究 备份 高速缓存 安全删除数据 文件压缩 Flash 文件系统 性能 可靠性和错误恢复 用户及文件系统 版本文件系统 安全

2021 Jul 17 · 2 min

现代操作系统::内存管理

内存管理 操作系统中管理分层存储器体系的部分称为存储管理器 (memory manager)。它的任务是有效地管理内存,即记录哪些内存是正在使用的,哪些内存是空闲的,在进程需要时为其分配内存,在进程使用完后释放内存。 无存储器抽象 最简单的存储器抽象就是根本没有抽象。 每一个程序都直接访问物理内存。在这种情况下,要想在内存中同时运行两个程序是不可能的。 在不使用存储器抽象的情况下运行多个程序 操作系统只需要把当前内存中所有内容保存到磁盘文件中,然后把下一个程序读入到内存中再运行即可。只要在某一个时间内存中只有一个程序,那么就不会发生冲突。 在特殊硬件的帮助下,即使没有交换功能,井发地运行多个程序也是可能的。 IBM 360 的早期模型是这样解决的:内存被划分为 2KB 的块,每个块被分配一个 4 位的保护键,保护键存储在 CPU 的特殊寄存器中。一个内存为 1MB 的机器只需要 512 个这样的 4 位寄存器,容量总共为 256 字节。PSW (Program Status Word, 程序状态字)中存有一个 4 位码。一个运行中的进程如果访问保护键与其 PSW 码不同的内存,360 的硬件会捕获到这一事件。因为只有操作系统可以修改保护键,这样就可以防止用户进程之间、用户进程和操作系统之间的互相干扰。引用了绝对物理地址会导致出错。 一种存储器抽象:地址空间 使用绝对物理地址的问题 用户可以访问每个地址,容易造成破坏 运行多个程序很困难 地址空间的概念 需要解决的问题 保护 重定向 地址空间是一个进程可用于寻址内存的一套地址集合。每个进程都有一个自己的地址空间,井且这个地址空间独立于其他进程的地址空间 基址寄存器和界限寄存器 使用动态重定位,简单地把每个进程的地址空间映射到物理内存的不同部分。 经典办法是给每个 CPU 配置两个特殊硬件寄存器,通常叫作基址寄存器和界限寄存器。当使用基址寄存器和界限寄存器时,程序装载到内存中连续的空闲位置且装载期间无须重定位,每次一个进程访问内存,取一条指令,读或写一个数据字,CPU 硬件会在把地址发送到内存总线前,自动把基址值加到进程发出的地址值上。同时,它检查程序提供的地址是否等于或大于界限寄存器里的值。如果访问的地址超过了界限,会产生错误并中止访问。 对基址寄存器和界限寄存器会以一定的方式加以保护,使得只有操作系统可以修改它们。 使用基址寄存器和界限寄存器重定位的缺点是,每次访问内存都需要进行加法和比较运算。比较运算可以做得很快,但是加法运算由于进位传递时间的问题,在没有使用特殊电路的情况下会显得很慢。 交换技术 内存容量优先,多进程存在内存超载的问题。 交换 即把一个进程完整调入内存,使该进程运行一段时间,然后把它存回磁盘。空闲进程主要存储在磁盘上,所以当它们不运行时就不会占用内存。 交换在内存中产生了多个空闲区 (hole, 也称为空洞),通过把所有的进程尽可能向下移动,有可能将这些小的空闲区合成一大块。该技术称为内存紧缩 (memory compaction)。通常不进行这个操作,因为它要耗费大量的 CPU 时间。 进程的数据段可以增长,系统如何分配内存。一种可用的方法是,当换入或移动进程时为它分配一些额外的内存。 空闲内存管理 使用位图的存储管理 使用位图方法时,内存可能被划分成小到几个字或大到几千字节的分配单元。每个分配单元对应于位图中的一位,0 表示空闲, 1 表示占用(或者相反) 分配单元的大小是一个重要的设计因素。分配单元越小,位图越大。 这种方法的主要问题是,在决定把一个占 k 个分配单元的进程调人内存时,存储管理器必须搜索位图,在位图中找出有 k 个连续 0 的串。查找位图中指定长度的连续 0 串是耗时的操作(因为在位图中该串可能跨越字的边界),这是位图的缺点。 使用链表的存储管理 维护一个记录已分配内存段和空闲内存段的链表。其中链表中的一个结点或者包含一个进程,或者是两个进程间的一块空闲区。 分配算法 首次适配 存储管理器沿若段链表进行搜索,直到找到一个足够大的空闲区,除非空闲区大小和要分配的空间大小正好一样,否则将该空闲区分为两部分,一部分供进程使用,另一部分形成新的空闲区。首次适配算法是一种速度很快的算法,因为它尽可能少地搜索链表结点。 下次适配 它的工作方式和首次适配算法法相同,不同点是每次找到合适的空闲区时都记录当时的位置,以便在下次寻找空闲区时从上次结束的地方开始搜索,而不是像首次适配算法那样每次都从头开始。 最佳适配 最佳适配算法搜索整个链表,找出能够容纳进程的最小的空闲区。最佳适配算法试图找出最接近实际需要的空闲区,以最好地匹配请求和可用空闲区,而不是先拆分一个以后可能会用到的大的空闲区。 因为每次调用最佳适配算法时都要搜索整个链表,所以它要比首次适配算法慢。让人感到有点意外的是,它比首次适配算法或下次适配算法浪费更多的内存,因为它会产生大址无用的小空闲区。 最差适配 总是分配最大的可用空闲区,使新的空闲区比较大从而可以继续使用。仿真程序表明最差适配算法也不是一个好主意。 快速适配 它为那些常用大小的空闲区维护单独的链表。 虚拟内存 每个程序拥有自己的地址空间,这个空间被分割成多个块,每一块称作一页或页面(page)。每一页有连续的地址范围。这些页被映射到物理内存,但井不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻执行必要的映射。当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的指令。 虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把 CPU 交给另一个进程使用. 分页 程序产生的这些地址称为虚拟地址 (virtual address) , 它们构成了一个虚拟地址空间 (virtual address space)。在没有虚拟内存的计算机上,系统直接将虚拟地址送到内存总线上,读写操作使用具有同样地址的物理内存字,而在使用虚拟内存的情况下,虚拟地址不是被直接送到内存总线上,而是被送到内存管理单元 (MemoryManagementUnit, MMU), MMU 把虚拟地址映射为物理内存地址。 虚拟地址空间按照固定大小划分成被称为页面 (page) 的若干单元 。在物理内存中对应的单元称为页框(page frame)。页面和页框的大小通常是一样的,实际系统中的页面大小从 512 字节到 1GB。 缺页中断或缺页错误 操作系统找到一个很少使用的页框且把它的内容写人磁盘(如果它不在磁盘上)。随后把需要访问的页面读到刚才回收的页框中,修改映射关系,然后重新启动引起陷阱的指令 输入的 16 位虚拟地址被分为 4 位的页号和 12 位的偏移址。 4 位的页号可以表示 16 个页面, 12 位的偏移可以为一页内的全部 4096 个字节编址。可用页号作为页表得出对应于该虚拟页面的页框号。如果"在/不在“ 位是 0, 则将引起一个操作系统陷阱。如果该位是 1, 则将在页表中查到的页框号复制到输出寄存器的高 3 位中,再加上输人虚拟地址中的低 12 位偏移址。如此就构成了 15 位的物理地址。输出寄存器的内容随即被作为物理地址送到内存总线。 页表 作为一种最简单的实现,虚拟地址到物理地址的映射可以概括如下:虚拟地址被分成虚拟页号(高位部分)和偏移址(低位部分)两部分 虚拟页号可用作页表的索引,以找到该虚拟页面对应的页表项。由页表项可以找到页框号(如果有的话)。然后把页框号拼接到偏移址的高位端,以替换掉虚拟页号,形成送往内存的物理地址。 页表项的结构是与机器密切相关的,但不同机器的页表项存储的信息都大致相同。 页框号 ”在/不在“位 保护位,指出一个页允许什么类型的访问,读写执行 修改位和访问位, 在写入一页时由硬件自动设置修改位。该位在操作系统重新分配页框时是非常有用的。如果一个页面已经被修改过(即它是"脏"的),则必须把它写回磁盘。如果一个页面没有被修改过(即它是“干净”的),则只简单地把它丢弃就可以了,因为它在磁盘上的副本仍然是有效的。这一位有时也被称为脏位 (dirty bit) , 因为它反映了该页面的状态。 不论是读还是写,系统都会在该页面被访问时设置访问位。它的值被用来帮助操作系统在发生缺页中断时选择要被淘汰的页面。不再使用的页面要比正在使用的页面更适合淘汰。 禁止高速缓存位,那些映射到设备寄存器而不是常规内存的页面而言,这个特性是非常重要的。假如操作系统正在紧张地循环等待某个 I/O 设备对它刚发出的命令作出响应,保证硬件是不断地从设备中读取数据而不是访问一个旧的被高速缓存的副本是非常重要的。通过这一位可以禁止高速缓存。具有独立的 I/O 空间而不使用内存映射 I/O 的机器不需要这一位。 加速分页过程 虚拟地址到物理地址的映射必须非常快。 如果虚拟地址空间很大,页表也会很大。 转换检测缓冲区 大多数程序总是对少址的页面进行多次的访问,而不是相反。因此,只有很少的页表项会被反复读取,而其他的页表项很少被访问。解决方案是为计算机设置一个小型的硬件设备,将虚拟地址直接映射到物理地址,而不必再访问页表。这种设备称为转换检测缓冲区(Translation Lookaside Buffer, TLB),有时又称为相联存储器(associate memory)或快表,它通常在 MMU 中,包含少量的表项,在实际中很少会超过 256 个。每个表项记录了一个页面的相关信息,包括虚拟页号、页面的修改位、 保护码(读/写/执行权限)和该页所对应的物理页框。 将一个虚拟地址放入 MMU 中进行转换时,硬件首先通过将该虚拟页号与 TLB 中所有表项同时(即并行)进行匹配,判断虚拟页面是否在其中。如果发现了一个有效的匹配并且要进行的访问操作并不违反保护位,则将页框号直接从 TLB 中取出而不必再访问页表。如果虚拟页号确实是在 TLB 中,但指令试图在一个只读页面上进行写操作,则会产生一个保护错误,就像对页表进行非法访间一样。 如果 MMU 检测到没有有效的匹配项,就会进行正常的页表查询。接着从 TLB 中淘汰一个表项,然后用新找到的页表项代替它。这样,如果这一页面很快被再次访问,第二次访问 TLB 时自然将会命中而不是未命中。当一个表项被清除出 TLB 时,将修改位复制到内存中的页表项,而除了访问位,其他的值不变。当页表项中从页表中装入 TLB 中时,所有的值都来自内存。 软件 TLB 管理 在现代很多机器上,几乎所有的页面管理都是在软件中实现的。在这些机器上,TLB 表项被操作系统显式地装载。当发生 TLB 访问失效时,不再是由 MMU 到页表中查找井取出需要的页表项,而是生成一个 TLB 失效并将问题交给操作系统解决。系统必须先找到该页面,然后从 TLB 中删除一个项,接着装载一个新的项,最后再执行先前出错的指令。当然,所有这一切都必须在有限的几条指令中完成,因为 TLB 失效比缺页中断发生得更加频繁。 如果 TLB 大到(如 64 个表项)可以减少失效率时,TLB 的软件管理就会变得足够有效。这种方法的最主要的好处是获得了一个非常简单的 MMU, 这就在 CPU 芯片上为高速缓存以及其他改善性能的设计腾出了相当大的空间。 软失效 一个页面访问在内存中而不在 TLB 中时,将产生软失效(softmiss)。那么此时所要做的就是更新一下 TLB, 不需要产生磁盘 I/O。典型的处理需要 10~20 个机器指令并花费几纳秒完成操作。 硬失效 页面本身不在内存中(当然也不在 TLB 中)时,将产生硬失效。此刻需要一次磁盘存取以装入该页面,这个过程大 概储要几亳秒。硬失效的处理时间往往是软失效的百万倍。在页表结构中查找相应的映射被称为页表遍历。 几种场景 所需的页面可能就在内存中,但却未记录在该进程的页表里。比如该页面可能已由其他进程从硬盘中调人内存,这种情况下只需要把所需的页面正确映射到页表中,而不用再从硬盘调入。这是一种典型的软失效,称为次要缺页错误 如果需要从硬盘重新调入页面,这就是严重缺页错误。 程序可能访问了一个非法地址,根本不需要向 TLB 中新增映射。此时,操作系统一般会通过报告段错误来终止该程序, 这种缺页属于程序错误 针对大内存的页表 多级页表 引人多级页表的原因是避免把全部页表一直保存在内存中。特别是那些从不需要的页表就不应该保留。 虚拟地址送入 MMU 后分为三个部分 PT1 用于顶级页表寻址,PT2 用于二级页表寻址,偏移量还是偏移量。以此扩展出多级页表 倒排页表 实际内存中的每个页框对应一个表项,而不是每个虚拟页面对应一个表项。例如,对千 64 位虚拟地址,4KB 的页, 4GB 的 RAM, 一个倒排页表仅需要 1 048 576 个表项。表项记录了哪一个(进程,虚拟页面)对定位于该页框。 从虚拟地址到物理地址的转换会变得很困难。它必须搜索整个倒排页表来查找某一个表项(n, p)。此外,该搜索必须对每一个内存访问操作都要执行一次,而不仅仅是在发生缺页中断时执行。每次内存访问操作都要查找一个 256K 的表不是一种使机器快速运行的方法 。 走出这种两难局面的办法是使用 TLB。如果 TLB 能够记录所有频繁使用的页面,地址转换就可能变得像通常的页表一样快。但是,当发生 TLB 失效时,需要用软件搜索整个倒排页表。实现该搜索的一个可行的方法是建立一张散列表,用虚拟地址来散列。当前所有在内存中的具有相同散列值的虚拟页面被链接在一起。如果散列表中的槽数与机器中物理页面数一样多,那么散列表的冲突链的平均长度将会是 1 个表项的长度,这将会大大提高映射速度。一旦页框号被找到,新的(虚拟页号,物理页框号)对就会被装载到 TLB 中。 倒排页表在 64 位机器中很常见,因为在 64 位机器中即使使用了大页面,页表项的数量还是很庞大的。 页面置换算法 最优页面置换算法 不可能实现 在缺页中断发生时,有些页面在内存中,其中有一个页面将很快被访问,其他页面则可能要到 10、100 或 1000 条指令后才会被访问,每个页面都可以用在该页面首次被访问前所要执行的指令数作为标记。 最优页面置换算法规定应该置换标记最大的页面。当缺页中断发生时,操作系统无法知道各个页面下一次 将在什么时候被访问。可以通过收集上一次的运行信息来判断,但是提升效率有限。 最近未使用页面置换算法 为使操作系统能够收集有用的统计信息,在大部分具有虚拟内存的计算机中,系统为每一页面设置了两个状态位。当页面被访问(读或写)时设置 R 位;当页面被写入(即修改)时设置 M 位。这些位包含在每个页表项中. 当启动一个进程时,它的所有页面的两个位都由操作系统设置成 0, R 位被定期地(比如在每次时钟中断时)清零,以区别最近没有被访问的页面和被访问的页面。 NRU (Not Recently Used, 最近未使用)算法随机地从没有被访问,没有被修改和没有被访问,已被修改的非空类中挑选一个页面淘汰。 先进先出页面置换算法 由操作系统维护一个所有当前在内存中的页面的链表,最新进入的页面放在表尾,最早进入的页面放在表头。当发生缺页中断时,淘汰表头的页面井把新调入的页面加到表尾。 第二次机会页面置换算法 FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问题,对该算法做一个简单的修改: 检查最老页面的 R 位。如果 R 位是 0, 那么这个页面既老又没有被使用,可以立刻置换掉,如果是 1 就将 R 位清 0, 井把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续搜索。 第二次机会算法就是寻找一个在最近的时钟间隔内没有被访问过的页面。 时钟页面置换算法 尽管第二次机会算法是一个比较合理的算法,但它经常要在链表中移动页面,既降低了效率又不是很有必要。 一个更好的办法是把所有的页面都保存在一个类似钟面的环形链表中,一个表针指向最老的页面 当发生缺页中断时,算法首先检查表针指向的页面,如果它的 R 位是 0 就淘汰该页面,并把新的页面插入这个位置,然后把表针前移一个位置,如果 R 位是 1 就清除 R 位并把表针前移一个位置。重复这个过程直到找到了一个 R 位为 0 的页面为止。 最近最少使用页面置换算法 在缺页中断发生时,置换未使用时间最长的页面。 实现 LRU, 需要在内存中维护一个所有页面的链表,最近最多使用的页面在表头,最近最少使用的页面在表尾。困难的是在每次访问内存时都必须要更新整个链表。在链表中找到一个页面,删除它,然后把它移动到表头是一个非常费时的操作,即使使用硬件实现也一样费时 用软件摸拟 LRU 一种可能的方案称为 NFU (NotFrequentlyUsed, 最不常用)算法,该算法将每个页面与一个软件计数器相关联,计数器的初值为 0。每次时钟中断时,由操作系统扫描内存中所有的页面,将每个页面的 R 位(它的值是 0 或 1) 加到它的计数器上。这个计数器大体上跟踪了各个页面被访问的频繁程度。发生缺页中断时,则置换计数器值最小的页面. 工作集页面置换算法 一个进程当前正在使用的页面的集合称为它的工作集 若内存太小而无法容纳下整个工作集,那么进程的运行过程中会产生大量的缺页中断导致运行速度也会变得很缓慢 若每执行几条指令程序就发生一次缺页中断,那么就称这个程序发生了颠簸 不少分页系统都会设法跟踪进程的工作集,以确保在让进程运行以前,它的工作集就已在内存中了。该方法称为工作集模型, 其目的在于大大减少缺页中断率。在进程运行前预先装入其工作集页面也称为预先调页 (prepaging)。 人们很早就发现大多数程序都不是均匀地访问它们的地址空间的,而访问往往是集中于一小部分页面。 一种近似的解法是进程的工作集可以被称为在过去的 r 秒内实际运行时间中它所访问过的页面的集合。 工作集时钟页面置换算法 当缺页中断发生后,需要扫描整个页表才能确定被淘汰的页面,因此基本工作集算法是比较费时的.有一种改进的算法,它基于时钟算法,井且使用了工作集信息,称为 WSClock (工作集时钟)算法 时钟算法一样,所需的数据结构是一个以页框为元素的循环表。最初,该表是空的。当装入第一个页面后,把它加到该表中。随若更多的页面的加入,它们形成一个环。每个表项包含来自基本工作集算法的上次使用时间,以及 R 位(已标明)和 M 位(未标明)。每次缺页中断时,首先检查指针指向的页面。如果 R 位被置为 1 该页面在当前时钟滴答中就被使用过,那么该页面就不适合被淘汰 。然后把该页面的 R 位设为 0, 指针指向下一个页面,井重复该算法。 页面置换算法小结 最优算法 不可实现,但可用作基准 NRU (最近未使用)算法 FIFO (先进先出)算法 第二次机会算法 比 FIFO 有较大的改善 时钟算法 现实的 LRU (最近最少使用)算法 很优秀,但很难实现 NFU (最不经常使用)算法 LRU 的相对粗略的近似 老化算法 LRU 的相对粗略的近似 工作集算法 非常近似 LRU 的有效算法 工作集时钟算法 好的有效算法 分页系统中的设计问题 局部分配策略与全局分配策略 三个进程 A、 B、 C 构成了可运行进程的集合。假如 A 发生了缺页中断,页面置换算法在寻找最近最少使用的页面时是只考虑分配给 A 的 6 个页面呢? 还是考虑所有在内存中的页面? 局部算法可以有效地为每个进程分配固定的内存片段。 全局算法在可运行进程之间动态地分配页框,因此分配给各个进程的页框数是随时间变化的. 全局算法在通常情况下工作得比局部算法好,当工作集的大小随进程运行时间发生变化时这种现象更加明显。 负载控制 一旦所有进程的组合工作集超出了内存容量,就可能发生颠簸。 减少竞争内存的进程数的一个好方法是将一部分进程交换到磁盘,并释放他们所占有的所有页面。 决定交换出哪个进程时不光要考虑进程大小和分页率,还要考虑它的特性(如它究竟是 CPU 密集型还是 I/O 密集型)以及其他进程的特性。 页面大小 要确定最佳的页面大小蒂要在几个互相矛盾的因素之间进行权衡。 随便选择一个正文段、数据段或堆栈段很可能不会恰好装满整数个页面,平均的情况下,最后一个页面中有一半是空的。多余的空间就被浪费掉了,这种浪费称为内部碎片 (internal fragmentation)。在内存中有 n 个段、页面大小为 p 字节时,会有 np/2 字节被内部碎片浪费。从这方面考虑,使用小页面更好。 考虑一个程序,它分成 8 个阶段顺序执行,每阶段需要 4KB 内存。如果页面大小是 32KB, 那就必须始终给程序分配 32KB 内存。如果页面大小是 16KB, 它就只需要 16KB。 如果页面大小是 4KB 或更小,那么在任何时刻它只需要 4KB 内存。总的来说,大尺寸页面比小尺寸页面浪费了更多内存 。 另一方面,页面小意味着程序需要更多的页面,这又意味着需要更大的页表。 内存与磁盘之间的传输一般是一次一页,传输中的大部分时间都花在了寻道和旋转延迟上,所以传输一个小页面所用的时间和传输一个大页面基本上是相同 小页面能够更充分地利用 TLB 空间 数学角度分析的结果 4KB 更适合 分离的指令空间和数据空间 早期计算机内存不够用的时候采取的设计,在使用这种设计的计算机中,两种地址空间都可以进行分页,而且互相独立。它们分别有自己的页表,分别完成虚拟页面到物理页框的映射。当硬件进行取指令操作时,它知道要使用指令空间和指令空间页表。 共享页面 那些只读的页面(诸如程序文本)可以共享,但是数据页面则不能共享。 只有实际修改的数据页面需要复制。这种方法称为写时复制,它通过减少复制而提高了性能。 共享库 当一个共享库被装载和使用时,整个库并不是被一次性地读人内存。而是根据需要,以页面为单位装载的,因此没有被调用到的函数是不会披装载到内存中的. 如果共享库中的一个函数因为修正一个 bug 被更新了,那么并不需要重新编译调用了这个函数的程序。旧的二进制文件依然可以正常工作。 内存映射文件 进程可以通过发起一个系统调用,将一个文件映射到其虚拟地址空间的一部分。在多数实现中,在映射共享的页面时不会实际读入页面的内容,而是在访问页面时才会被每次一页地读入,磁盘文件则被当作后备存储。当进程退出或显式地解除文件映射时,所有被改动的页面会被写回到磁盘文件中。 清除策略 通过一个分页守护进程定时把修改过的页写回到磁盘 一种实现清除策略的方法就是使用一个双指针时钟。前指针由分页守护进程控制.当它指向一个脏页面时,就把该页面写回磁盘,前指针向前移动。当它指向一个干净页面时,仅仅指针向前移动. 后指针用于页面置换,就像在标准时钟算法中一样。现在,由于分页守护进程的工作,后指针命中干净页面的概率会增加。 虚拟内存接口 对于一些高级系统而言,程序员可以对内存映射进行控制,并可以通过非常规的方法来增强程序的行为。 过让一个进程把一片内存区域的名称通知另一个进程,而使得第二个进程可以把这片区域映射到它的虚拟地址空间中去。通过两个进程(或者更多)共享同一部分页面,高带宽的共享就成为可能 分布式共享内存:允许网络上的多个进程共享一个页面集合 有关实现的问题 与分页有关的工作 时间点 进程创建 确定程序和数据在初始时有多大,井为它们创建一个页表。 在内存中为页表分配空间并对其进行初始化。当进程被换出时,页表不需要驻留在内存中,但当进程运行时,它必须在内存中。 操作系统要在磁盘交换区中分配空间,以便在一个进程换出时在磁盘上有放置此进程的空间。 操作系统必须把有关页表和磁盘交换区的信息存储在进程表中。 进程执行 为新进程重置 MMU, 刷新 TLB, 以清除以前的进程遗留的痕迹。 新进程的页表成为当前页表 缺页中断 通过读硬件寄存器来确定是哪个虚拟地址造成了缺页中断 计箕蒂要哪个页面,并在磁盘上对该页面进行定位。它必须找到合适的页框来存放新页面,必要时还要置换老的页面,然后把所需的页面读入页框。 回退程序计数器,使程序计数器指向引起缺页中断的指令,并重新执行该指令。 进程终止 操作系统必须释放进程的页表、页面和页面在硬盘上所占用的空间。 如果某些页面是与其他进程共享的,当最后一个使用它们的进程终止的时候,才可以释放内存和磁盘上的页面。 缺页中断处理 硬件陷入内核,在堆栈中保存程序计数器。 启动一个汇编代码例程保存通用寄存器和其他易失的信息,以免被操作系统破坏。 当操作系统发现一个缺页中断时,尝试发现需要哪个虚拟页面 一旦知道了发生缺页中断的虚拟地址,操作系统检查这个地址是否有效,并检查存取与保护是否一致。如果不一致,向进程发出一个信号或杀掉该进程。如果地址有效且没有保护错误发生,系统则检查是否有空闲页框。如果没有空闲页框,执行页面置换算法寻找一个页面来淘汰。 如果选择的页框"脏”了,安排该页写回磁盘,并发生一次上下文切换,挂起产生缺页中断的进程,让其他进程运行直至磁盘传输结束。无论如何,该页框被标记为忙,以免因为其他原因而被其他进程占用。 一且页框“干净”后(无论是立刻还是在写回磁盘后),操作系统查找所需页面在磁盘上的地址,通过磁盘操作将其装入。该页面正在被装入时,产生缺页中断的进程仍然被挂起,井且如果有其他可运行的用户进程,则选择另一个用户进程运行。 当磁盘中断发生时,表明该页已经被装人,页表已经更新可以反映它的位置,页框也被标记为正常状态。 恢复发生缺页中断指令以前的状态,程序计数器重新指向这条指令。 调度引发缺页中断的进程,操作系统返回调用它的汇编语言例程. 该例程恢复寄存器和其他状态信息,返回到用户空间继续执行,就好像缺页中断没有发生过 一样 。 指令备份 当程序访问不在内存中的页面时,引起缺页中断的指令会半途停止并引发操作系统的陷阱 。在操作系统取出所需的页面后,它需要重新启动引起陷阱的指令。 通过使用一个隐藏的内部寄存器。在每条指令执行之前,把程序计数器的内容复制到该寄存器。 锁定内存中的页面 一种解决方法是锁住正在做 I/O 操作的内存中的页面以保证它不会被移出内存。锁住一个页面通常称为在内存中钉住 (pinning)页面.另一种方法是在内核缓冲区中完成所有的 I/O 操作,然后再将数据复制到用户页面。 后备存储 页面被换出时会存放在磁盘上的哪个位置 最简单的算法是在磁盘上设置特殊的交换分区,甚至从文件系统划分一块独立的磁盘。 策略和机制的分离 控制系统复杂度的一种重要方法就是把策略从机制中分离出来。通过使大多数存储管理器作为用户级进程运行,就可以把该原则应用到存储管理中。 分段 对许多问题来说,有两个或多个独立的地址空间可能比只有一个要好得多。 一个直观并且通用的方法是在机器上提供多个互相独立的称为段(segment)的地址空间。每个段由一个从 0 到最大的线性地址序列构成。 段是一个逻辑实体,程序员知道这一点并把它作为一个逻辑实体来使用。一个段可能包括一个过程、一个数组、一个堆栈、一组数值变址,但一般它不会同时包含多种不同类型的内容。 分段也有助于在几个进程之间共享过程和数据。这方面一个常见的例子就是共享库. 不同的段可以有不同种类的保护 。一个过程段可以被指明为只允许执行,从而禁止对它的读出和写入。 分段和分页的实现本质上是不同的: 页面是定长的而段不是。 在系统运行一段时间后内存被划分为许多块,一些块包含着段,一些则成了空闲区,这种现象称为棋盘形碎片或外部碎片 如果一个段比较大,把它整个保存在内存中可能很不方便甚至是不可能的,因此产生了对它进行分页的想法。这样,只有那些真正需要的页面才会被调入内存。 有关内存管理的研究 内存管理中具有重启功能的虚拟机 应用程序向系统提供决策,以指导取哪个物理页来支持虚拟页 云服务器稳定性对页面处理的影响方面

2021 Jul 16 · 3 min

现代操作系统::进程与线程

进程与线程 进程 一个进程就是一个正在执行程序的实例,包括程序计数器、寄存器和变量的当前值。 进程的创建 4 种主要事件会导致进程的创建: 系统初始化 正在运行的程序执行了创建进程的系统调用 用户请求创建一个新进程 一个批处理作业的初始化 前台进程,与用户交互完成工作的进程 后台进程,与特定的用户没有关系,完成专门功能的进程,守护进程 在 UNIX 系统中,只有一个系统调用可以用来创建新进程:fork。这个系统调用会创建一个与调用进程相同的副本。在调用了 fork 后,这两个进程(父进程和子进程)拥有相同的内存映像、同样的环境字符串和同样的打开文件。这就是全部情形。通常 ,子进程接着执行 execve 或一个类似的系统调用,以修改其内存映像并运行一个新的程序。之所以要安排两步建立进程,是为了在 fork 之后但在 execve 之前允许该子进程处理其文件描述符,这样可以完成对标准输入文件、标准输出文件和标准错误文件的重定向。 在 UNIX 中,子进程的初始地址空间是父进程的一个副本,但是这里涉及两个不同的地址空间,不可写的内存区是共享的。某些 UNIX 的实现使程序正文在两者间共享,因为它不能被修改。或者,子进程共享父进程的所有内存,但这种情况下内存通过写时复制 (copy-on-write) 共享,这意味着一且两者之一想要修改部分内存,则这块内存首先被明确地复制,以确保修改发生在私有内存区域。 进程的终止 通常有以下几种情况会导致进程的终止 正常退出(自愿的)。 出错退出(自愿的). 严重错误(非自愿)。 被其他进程杀死(非自愿). 进程的层次结构 某些系统中,当进程创建了另一个进程后,父进程和子进程就以某种形式继续保持关联。子进程自身可以创建更多的进程,组成一个进程的层次结构。 在 UNIX 中,进程和它的所有子进程以及后裔共同组成一个进程组。 进程的状态 运行态(该时刻进程实际占用 CPU)。 就绪态(可运行,但因为其他进程正在运行而暂时停止)。 阻塞态(除非某种外部事件发生,否则进程不能运行). 进程的实现 操作系统维护进程表 每个进程占用一个表项目,称为进程控制块,里面有程序运行的必要信息。 所有的中断都从保存寄存器开始,对于当前进程而言,通常是保存在进程表项中。 当一个 I/O 中断发生时,中断硬件将程序计数器、程序状态字、有时还有一个或多个寄存器压入堆栈,计算机随即跳转到中断向量所指示的地址。这些是硬件完成的所有操作,然后软件,特别是中断服务例程就接管一切剩余的工作。 中断发生后操作系统最底层的工作步骤 硬件压人堆栈程序计数器等 硬件从中断向量装人新的程序计数器 汇编语言过程保存寄存器值 汇编语言过程设置新的堆栈 C 中断服务例程运行 (典型地读和缓冲输入) 调度程序决定下一 个将运行的进程 C 过程返回至汇编代码 汇编语言过程开始运行新的当前进程 一个进程在执行过程中可能被中断数千次,但关键是每次中断后被中断的进程都返回到与中断发生前完全相同的状态。 多道程序设计模型 采用多道程序设计可以提高 CPU 的利用率。 假设一个进程等待 I/O 操作的时间与其停留在内存中时间的比为 p。当内存中同时有 n 个进程时、则所有 n 个进程都在等待 I/O (此时 CPU 空转)的概率是\( p^n \)。CPU 的利用率由下面的公式给出: \(CPU 利用率=1-p^n\) 线程 线程的使用 人们需要多线程的主要原因是,在许多应用中同时发生若多种活动。其中某些活动随着时间的推移会被阻塞。通过将这些应用程序分解成可以准井行运行的多个顺序线程,程序设计校型会变得更简单。 在有了多线程概念之后,我们才加入了一种新的元素: 并行实体拥有共享同一个地址空间和所有可用数据的能力 线程比进程更轻量级,所以它们比进程更容易(即更快)创建,也更容易撤销。许多系统中,创建一个线程较创建一个进程要快 1~100 倍 如果存在着大量的计算和大量的 I/O 处理,拥有多个线程允许这些活动彼此重叠进行,从而会加快应用程序执行的速度。 在多 CPU 系统中,多线程是有益的,在这样的系统中,真正的并行有了实现的可能 经典的线程模型 进程模型基于两种独立的概念:资源分组处理与执行。 进程有存放程序正文和数据以及其他资源的地址空间。这些资源中包括打开的文件、子进程、即将发生的定时器、信号处理程序、账号信息等。把它们都放到进程中可以更容易管理。 进程拥有一个执行的线程,通常简写为线程 (thread)。在线程中有一个程序计数器,用来记录接着要执行哪一条指令。线程拥有寄存器,用来保存线程当前的工作变量。线程还拥有一个堆栈,用来记录执行历史,其中每一帧保存了一个已调用的但是还没有从中返回的过程。尽管线程必须在某个进程中执行.但是线程和它的进程是不同的概念,并且可以分别处理。进程用于把资源集中到一起,而线程则是在 CPU 上被调度执行的实体。 在同一个进程环境中,允许彼此之间有较大独立性的多个线程执行。在同一个进程中并行运行多个线程,是对在同一台计算机上并行运行多个进程的模拟。 由于线程具有进程的某些性质,所以有时被称为轻量级进程 一些 CPU 已经有直接硬件支持多线程,并允许线程切换在纳秒级完成。 由于各个线程都可以访问进程地址空间中的每一个内存地址,所以一个线程可以读、写或甚至清除另一个线程的堆栈。线程之间是没有保护的。不可能也没有必要实现保护,实现多线程的初衷就是为了贡献资源并协作。 每个进程中的内容 地址空间 全局变量 打开文件 子进程 定时器 信号和信号处理程序 账户信息 每个线程中的内容 程序计数器 寄存器 堆栈 状态 线程概念试图实现的是,共享一组资源的多个线程的执行能力,以便这些线程可以为完成某一任务而共同工作 。 和传统进程一样(即只有一个线程的进程),线程可以处于若干种状态的任何一个:运行、阻塞、就绪或终止。 每个线程有其自己的堆栈,每个线程的堆栈有一帧,供各个被调用但是还没有从中返回的过程使用。在该栈帧中存放了相应过程的局部变量以及过程调用完成之后使用的返回地址。 多线程的情况下,进程通常会从当前的单个线程开始。这个线程有能力通过调用一个库函数(如 thread_create)创建新的线程 创建线程通常都返回一个线程标识符,该标识符就是新线程的名字。 另一个常见的线程调用是 thread_yield, 它允许线程自动放弃 CPU 从而让另一个线程运行。因为不同于进程,(线程库)无法利用时钟中断强制线程让出 CPU。所以设法使线程行 为“高尚”起来,并且随着时间的推移自动交出 CPU, 以便让其他线程有机会运行,就变得非常重要。 复杂性 如果子进程拥有了与父进程一样的多个线程,如果父进程在 read 系统调用(比如键盘)上被阻塞了会发生什么情况? 另一类问题和线程共享许多数据结构的事实有关。如果一个线程关闭了某个文件,而另一个线程还在该文件上进行读操作时会怎样?假设有一个线程注意到几乎没有内存了,并开始分配更多的内存。在工作一半的时候,发生线程切换,新线程也注意到几乎没有内存了,并且也开始分配更多的内存。这样,内存可能会被分配两次。 POSIX 线程 为实现可移植的线程程序,IEEE 在 IEEE 标准 1003.lc 中定义了线程的标准。它定义的线程包叫作 pthread。 主要函数特性 pthread_create,创建一个新的线程,新创建的线程的线程标识符会作为函数值返回 pthread_exit,终止该线程并释放它的栈。 pthread_join,用来等待别的特定线程的终止。而要等待线程的线程标识符作为一个参数给出。 pthread_yield,让出 CPU 时间片 pthread_attr_init,建立关联一个线程的属性结构井初始化成默认值 pthread_attr_destroy,删除一个线程的属性结构,释放它占用的内存 在用户空间实现线程 内核无感知,从内核角度考虑,就是按正常的方式管理,即单线程进程。 用户级线程包可以在不支持线程的操作系统上实现 在用户空间管理线程时,每个进程需要有其专用的线程表,工作方式于进程表类似 切换速度快,不需要陷入内核,不需要上下文切换,也不需要对内存高速缓存进行刷新,这就使得线程调度非常快捷。 允许每个进程有自己定制的调度算法 扩展性好,内核空间线程需要一些固定表格空间和堆栈空间。 实现阻塞调用难度高 缺页中断,导致内核中断整个进程直到 I/O 完成 没有时钟中断,调度问题 在内核空间实现线程 内核中有用来记录系统中所有线程的线程表 内核的线程表保存了每个线程的寄存器、状态和其他信息。 所有能够阻塞线程的调用都以系统调用的形式实现 在内核中创建或撤销线程的代价比较大,某些系统采取“环保"的处理方式,回收其线程。当某个线程被撤销时,就把它标志为不可运行的,但是其内核数据结构没有受到影响。稍后,在必须创建一个新线程时,就重新启动某个旧线程,从而节省了一些开销。 内核线程不需要任何新的、非阻塞系统调用 信号是发给进程而不是线程的,当多个线程注册了同一个信号会发生什么? 混合实现 人们已经研究了各种试图将用户级线程的优点和内核级线程的优点结合起来的方法。一种方法是使用内核级线程,然后将用户级线程与某些或者全部内核线程多路复用起来。内核只识别内核级线程,并对其进行调度。其中一些内核级线程会被多个用户级线程多路复用。如同在没有多线程能力操作系统中某个进程中的用户级线程 一样,可以创建、撤销和调度这些用户级线程。在这种梭型中,每个内核级线程有一个可以轮流使用的用户级线程集合. 调度程序激活机制 调度程序激活工作的目标是模拟内核线程的功能,但是为线程包提供通常在用户空间中才能实现的更好的性能和更大的灵活性。 当使用调度程序激活机制时,内核给每个进程安排一定数量的虚拟处理器,并且让(用户空间)运行时系统将线程分配到处理器上。 使该机制工作的基本思路是,当内核了解到一个线程被阻塞之后,内核通知该进程的运行时系统,并且在堆栈中以参数形式传递有问题的线程编号和所发生事件的一个描述。内核通过在一个已知的起始地址启动运行时系统,从而发出了通知,这是对 UNIX 中信号的一种粗略模拟。这个机制称为上行调用(upcall)。 违反分层次系统内在结构 弹出式线程 一个消息的到达导致系统创建一个处理该消息的线程,这种线程称为弹出式线程 对该新线程指定所要处理的消息。使用弹出式线程的结果是.消息到达与处理开始之间的时间非常短。 使单线程代码多线程化 一些容易犯的错误 多线程读写全局变量 可重入问题 信号共享问题 堆栈的管理 进程间通信 需要解决的问题 一个进程如何把信息传递给另一个 确保两个或更多的进程在关键活动中不会出现交叉 保证相互关联的进程执行的顺序正确 竞争条件 两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序,称为竞争条件 临界区 在某些时候进程可能需要访问共享内存或共享文件,或执行另外一些会导致竞争的操作。我们把对共享内存进行访问的程序片段称作临界区域 (critical region) 或 临界区。 需要满足的条件 任何两个进程不能同时处于其临界区 不应对 CPU 的速度和数量做任何假设 临界区外运行的进程不得阻塞其他进程 不得使进程无限期等待进人临界区 忙等待的互斥 几种实现互斥的方案 屏蔽中断 每个进程在刚刚进入临界区后立即屏蔽所有中断,并在就要离开之前再打开中断。屏蔽中断后,时钟中断也被屏蔽,阻止了进程切换,在多 CPU 下不适用,而且低效。 锁变量 设想有一个共享(锁)变量,其初始值为 0。当一个进程想进人其临界区时,它首先测试这把锁。 读锁与写锁操作不具有原子性 严格轮换法 整型变量 turn,初始值为 0,用于记录轮到哪个进程进入临界区,井桧查或更新共享内存。开始时,进程 0 检查 turn,发现其值为 0, 于是进入临界区。进程 1 也发现其值为 0,所以在一个等待循环中不停地测试 turn. 看其值何时变为 1。连续测试一个变量 直到某个值出现为止,称为忙等待。只有当认为当等待时间是很短的时候才使用这种方案 用于忙等待的锁,称为自选锁。 Peterson 解法 在使用共享变及(即进入其临界区)之前,各个进程使用其进程号 0 或 1 作为参数来调用 enter_region。该调用在需要时将使进程等待,直到能安全地进入临界区。在完成对共享变量的操作之后,进程将调用 leave_region, 表示操作已完成,若其他的进程希望进入临界区,则现在就可以进入。 TSL 指令 TSL RX, LOCK 称为测试并加锁。 它将一个内存字 lock 读到寄存器 RX 中,然后在该内存地址上存一个非零值。读字和写字操作保证是不可分割的,即该指令结束之前其他处理器均不允许访问该内存字。执行 TSL 指令的 CPU 将锁住内存总线,以禁止其他 CPU 在本指令结束之前访问内存。 当操作结束时,进程用一条普通的 move 指令将 lock 的值重新设置为 0。 锁住存储总线不同干屏蔽中断。屏蔽中断,然后在读内存字之后跟右写操作井不能阻止总线上的第二个处理器在读操作和写操作之间访问该内存字。 一个可替代 TSL 的指令是 XCHG, 它原子性地交换了两个位置的内容 睡眠与唤醒 Peterson 解法和 TSL 或 XCHG 解法有忙等待的缺点。 低优先级进程无法离开临界区,优先级反转问题 sleep 原语,wakeup 原语 信号量 提供一个整型变让来累计唤醒次数,供以后使用。一个信号量的取值可以为 0 (表示没有保存下来的唤醒操作)或者为正值(表示有一个或多个唤醒操作)。 PV 操作: P 表示尝试 , V 表示升高 原子操作,是指一组相关联的操作要么都不间断地执行,要么都不执行。 对一信号量执行 down 橾作,则是检查其值是否大干 0。若该值大于 0, 则将其值减 1 井继续;若该值为 0, 则进程将睡眠 up 操作作对信号量的值增 1。如果一个或多个进程在该信号众上睡眠,无法完成一个先前的 down 操作, 则由系统选择其中的一个并允许该进程完成它的 down 操作。于是,对一个有进程在其上睡眠的信号量执行一次 up 操作之后,该信号量的值仍旧是 0, 但在其上睡眠的进程却少了一个。 信号量的另一种用途是用于实现同步 互斥量 信号量的一个简化版本,称为互斥量 互斥量是一个可以处于两态之一的变量:解锁和加锁。这样,只需要一个二进制位表示它,不过实际上,常常使用一个整型量, 0 表示解锁,而其他所有的值则表示加锁 。互斥量使用两个过程。当一个线程(或进程)需要访问临界区时,它调用 mutex_lock。如果该互斥量当前是解锁的(即临界区可用). 此调用成功,调用线程可以自由进入该临界区。 另一方面,如果该互斥呈已经加锁,调用线程被阻塞,直到在临界区中的线程完成井调用 mutex_unlock。如果多个线程被阻塞在该互斥量上,将随机选择一个线程井允许它获得锁. 当获取 mutex_lock 失败,线程调用 thread_yield 将 CPU 放弃给另一个线程。 多个进程如何互斥 第一种,有些共享数据结构,如信号量,可以存放在内核中,并且只能通过系统调用来访问。 第二种,多数现代操作系统 (包括 UNIX 和 Windows) 提供一种方法,让进程与其他进程共享其部分地址空间。在这种方法中,缓冲区和其他数据结构可以共享。在最坏的情形下,如果没有可共享的途径,则可以使用共享文件。 快速用户区互斥量 futex futex 由一块能够被多个进程共享的内存空间(一个对齐后的整型变量)组成 实现了基本的锁(很像互斥锁),但避免了陷入内核,除非它真的不得不这样做。 一个 futex 包含两个部分 : 一个内核服务和一个用户库。内核服务提供一个等待队列,它允许多个进程在一个锁上等待。 没有竞争时,futex 完全在用户空间工作。 如果该锁被另一个线程持有,那么线程必须等待。这种情况下,futex 库不自旋,而是使用一个系统调用把这个线程放在内核的等待队列上。 当一个线程使用完该锁,它通过原子操作“增加并检验”来释放锁,并检查结果,看是否仍有进程阻塞在内核等待队列上。如果有,它会通知内核可以对等待队列里的一个或多个进程解除阻塞。如果没有锁竞争,内核则不需要参与其中。 pthread 中的互斥量 pthread 提供许多可以用来同步线程的函数。其基本机制是使用一个可以被锁定和解锁的互斥量来保护每个临界区。 互斥量在允许或阻塞对临界区的访问上是很有用的,条件变量则允许线程由于一些未达到的条件而阻塞。 管程 一个管程是一个由过程、变量及数据结构等组成的一个集合,它们组成一个特殊的模块或软件包。进程可在任何需要的时候调用管程中的过程,但它们不能在管程之外声明的过程中直接访问管程内的数据结构。 管程有一个很重要的特性.即任一时刻管程中只能有一个活跃进程,这一特性使管程能有效地完成互斥。 典型的处理方法是,当一个进程调用管程过程时,该过程中的前几条指令将检查在管程中是否有其他的活跃进程。如果有,调用进程将被挂起,直到另一个进程离开管程将其唤醒。如果没有活跃进程在使用管程,则该调用进程可以进入。 进入管程时的互斥由编译器负责,但通常的做法是用一个互斥量或二元信号量。 java 的 synchronized 就是一种管程的实现 用于处理共享内存的访问问题,对于多 CPU 和分布式系统,需要额外的手段。 消息传递 这种进程间通信的方法使用两条原语 send 和 receive, 它们像信号量而不像管程,是系统调用而不是语言成分。 前一个调用向一个给定的目标发送一条消息,后一个调用从一个给定的源接收一条消息。如果没有消息可用,则接收者可能被阻塞,直到一条消息到达,或者,带着一个错误码立即返回 。 设计要点 确认机制 重发机制 消息幂等 身份认证 一个著名的消息传递系统是消息传递接口(MPI) 屏障 当一个进程到达屏障时,它就被屏障阻拦,直到所有进程都到达该屏障为止。屏障可用于一组进程同步, 避免锁:读-复制-更新 在某些情况下,我们可以允许写操作来更新数据结构,即便还有其他的进程正在使用它。窍门在干确保每个读操作要么读取旧的数据版本,要么读取新的数据版本,但绝不能是新旧数据的一些奇怪组合。 调度 当计算机系统是多道程序设计系统时,通常就会有多个进程或线程同时竞争 CPU。只要有两个或更多的进程处于就绪状态,这种情形就会发生。如果只有一个 CPU 可用,那么就必须选择下一个要运行的进程。在操作系统中,完成选择工作的这一部分称为调度程序(scheduler), 该程序使用的算法称为调度算法(scheduling algorithm)。 调度简介 几乎所有进程的(磁盘或网络) I/O 请求和计箕都是交替突发的 典型的计算密集型进程具有较长时间的 CPU 集中使用和较小频度的 I/O 等待。 I/O 密集型进程具有较短时间的 CPU 集中使用和频繁的 I/O 等待。 它是 I/O 类的,因为这种进程 在 I/O 请求之间较少进行计算,并不是因为它们有特别长的 I/O 请求。在 I/O 开始后无论处理数据是多还是少,它们都花费同样的时间提出硬件请求读取磁盘块 。 随着 CPU 变得越来越快,更多的进程倾向为 I/O 密集型 何时调度 在创建一个新进程之后,需要决定是运行父进程还是运行子进程。 在一个进程退出时必须做出调度决策。一个进程不再运行(因为它不再存在),所以必须从就绪进程集中选择另外某个进程。如果没有就绪的进程,通常会运行一个系统提供的空闲进程。 当一个进程阻塞在 I/O 和信号量上或由于其他原因阻塞时,必须选择另一个进程运行。 在一个 I/O 中断发生时,必须做出调度决策。 非抢占式调度算法挑选一个进程,然后让该进程运行直至被阻塞,或者直到该进程自动释放 CPU.即使该进程运行了若干个小时,它也不会被强迫挂起。这样做的结果是,在时钟中断发生时不会进行调度。在处理完时钟中断后,如果没有更高优先级的进程等待到时,则被中断的进程会继续执行 。 抢占式调度算法挑选一个进程,井且让该进程运行某个固定时段的最大值。如果在该时段结束时,该进程仍在运行,它就被挂起,而调度程序挑选另一个进程运行。进行抢占式调度处理,需要在时间间隔的末端发生时钟中断,以便把 CPU 控制返回给调度程序。如果没有可用的时钟,那么非抢占式调度就是唯一的选择了 。 调度算法分类 批处理 批处理系统在商业领域仍在广泛应用,用来处理薪水册、存货清单、账目收入、账目支出、利息计算(在银行)、索赔处理(在保险公司)和其他的周期性的作业 。 减少了进程的切换从而改善了性能 交互式 为了避免一个进程霸占 CPU 拒绝为其他进程服务,抢占是必需的 抢占也是必要的。服务器也归于此类,因为通常它们要服务多个突发的(远程)用户。 实时 在有实时限制的系统中,抢占有时是不需要的,因为进程了解它们可能会长时间得不到运行,所以通常很快地完成各自的工作并阻塞。实时系统与交互式系统的差别是,实时系统只运行那些用来推进现有应用的程序,而交互式系统是通用的,它可以运行任意的非协作甚至是有恶意的程序。 调度算法的目标 公平一一给每个进程公平的 CPU 份额 策略强制执行一一保证规定的策略被执行 平衡一一保持系统的所有部分都忙碌 批处理系统 吞吐量一一每小时最大作业数 周转时间一一从提交到终止间的最小时间 CPU 利用率一一保持 CPU 始终忙碌 交互式系统 响应时间一一快速响应请求 均衡性一一满足用户的期望 实时系统 满足截止时间一避免丢失数据 可预测性一一在多媒体系统中避免品质降低 批处理系统中的调度 先来先服务 最短作业优先 最短剩余时间优先 交互式系统中的调度 轮转调度 每个进程被分配一个时间段,称为时间片 (quantum), 即允许该进程在该时间段中运行。如果在时间片结束时该进程还在运行,则将剥夺 CPU 井分配给另一个进程。如果该进程在时间片结束前阻塞或结束,则 CPU 立即进行切换。 时间片轮转调度很容易实现,调度程序所要做的就是维护一张可运行进程列表 时间片轮转调度中唯一有趣的一点是时间片的长度。从一个进程切换到另一个进程是需要一定时间进行管理事务处理的,更新各种表格和列表、消除和重新调入内存高速缓存等。时间片的长度影响 CPU 效率。 优先级调度 每个进程被赋予一个优先级,允许优先级最高的可运行进程先运行。 为了防止高优先级进程无休止地运行下去,调度程序可能在每个时钟滴答(即每个时钟中断)降低当前进程的优先级。 另一种方法是,给每个进程赋予一个允许运行的最大时间片,当用完这个时间片时,次高优先级的进程便获得运行机会。 多级队列 设立优先级类。属于最高优先级类的进程运行一个时间片,属于次高优先级类的进程运行 2 个时间片,再次一级运行 4 个时间片,以此类推。当一个进程用完分配的时间片后,它被移到下一类。 最短进程优先 由于最短作业优先常常伴随君最短响应时间,所以如果能够把它用千交互进程,那将是非常好的。 保证调度 为了实现所做的保证,系统必须跟踪各个进程自创建以来已使用了多少 CPU 时间。然后它计箕各个 进程应获得的 CPU 时间,即自创建以来的时间除以 n。由于各个进程实际获得的 CPU 时间是已知的,所以很容易计算出真正获得的 CPU 时间和应获得的 CPU 时间之比。比率为 0.5 说明一个进程只获得了应得时间的一半,而比率为 2.0 则说明它获得了应得时间的 2 倍。于是该算法随后转向比率最低的进程,直到该进程的比率超过它的最接近竞争者为止。 彩票调度 进程提供各种系统资源(如 CPU 时间)的彩禀。 一旦需要做出一项调度决策时,就随机抽出一张彩票,拥有该彩票的进程获得该资源。在应用到 CPU 调度时,系统可以掌握每秒钟 50 次的一种彩票,作为奖励每个获奖者可以得到 20ms 的 CPU 时间。 彩栗调度可以用来解决用其他方法很难解决的问题。一个例子是,有一个视频服务器,在该视频服务器上若干进程正在向其客户提供视频流,每个视频流的帧速率都不相同。假设这些进程需要的帧速率分别是 1O、20 和 25 帧/秒。如果给这些进程分别分配 10、 20 和 25 张彩票,那么它们会自动地按照大致正确的比例(即 10:20:25)划分 CPU 的使用。 公平分享调度 某些系统在调度处理之前考虑谁拥有进程这一因素。在这种模式中,每个用户分配到 CPU 时间的一部分,而调度程序以一种强制的方式选择进程。这样,如果两个用户都得到获得 50% CPU 时间的保证,那么无论一个用户有多少进程存在,每个用户都会得到应有的 CPU 份额。 实时系统中的调度 实时系统是一种时间起着主导作用的系统。 计算机必须在一个确定的时间范围内恰当地做出反应 实时系统通常可以分为硬实时 (hard real time) 和软实时 (soft real time) , 前者的含义是必须满足绝对的截止时间,后者的含义是虽然不希望偶尔错失截止时间,但是可以容忍。在这两种情形中,实时性能都是通过把程序划分为一组进程而实现的,其中每个进程的行为是可预测和提前掌握的。这些进程 一般寿命较短,并且极快地运行完成。在检测到一个外部信号时 , 调度程序的任务就是按照满足所有截止时间的要求调度进程。 策略和机制 对于一个进程有许多子进程井在其控制下运行,调度程序无法知晓哪个进程最重要,解决问题的方法是将调度机制与调度策略分离这个一贯的原则。也就是将调度算法以某种形式参数化,而参数可以由用户进程填写。 线程调度 当若干进程都有多个线程时,就存在两个层次的并行:进程和线程。在这样的系统中调度处理有本质差别,这取决于所支持的是用户级线程还是内核级线程(或两者都支持)。 用户级线程 内核调度进程,进程选择线程 内核级别线程 内核选择一个特定的线程运行。它不用考虑该线程属千哪个进程,不过如果有必要的话,它可以这样做。对被选择的线程赋予一个时间片,而且如果超过了时间片,就会强制挂起该线程。 用户级线程和内核级线程之间的差别在于性能。用户级线程的线程切换需要少址的机器指令,而内核级线程垢要完整的上下文切换,修改内存映像,使高速缓存失效,这导致了若干数量级的延迟。另一方面,在使用内核级线程时,一且线程阻塞在 I/O 上就不需要像在用户级线程中那样将整个进程挂起 。 经典 IPC 问题 哲学家就餐问题 读者-写者问题 有关进程于线程的研究 多处理器上的线程集群 进程执行过程的记录和重放 调度间题 移动设备上的低能耗调度、超线程级调度

2021 Jul 15 · 3 min

现代操作系统::引论

引论 什么是操作系统 操作系统是一种运行在内核态的软件 为应用程序提供一个资源集的清晰抽象,并管理这些硬件资源,而不仅仅是一堆硬件 作为扩展机器的操作系统,创建好的抽象,并实现和管理它所创建的抽象对象。 作为资源管理者的操作系统,在相互竞争的程序之间有序地控制对处理器、存储器以及其他 I/O 接口设备的分配。 操作系统的历史 第一代 (1945 ~ 1955) : 真空管和穿孔卡片 第二代 (1955 ~ 1965) : 晶体管和批处理系统 第三代 (1965 ~ 1980) : 集成电路和多道程序设计 第四代 (1980 ~ ) :个人计算机 第五代 (1990 ~ ): 移动计算机 计算机硬件简介 处理器 专门的指令集 寄存器 程序计数器 保存了将要取出的下一条指令的内存地址 堆栈指针 指向内存中当前栈的顶端 程序状态字 这个寄存器包含了条件码位、CPU 优先级、模式(用户态或内核态),以及各种其他控制位 通用寄存器 流水线 个 CPU 可以有单独的取指单元、解码单元和执行单元,于是当它执行指令 n 时,还可以对指令 n + 1 解码,井且读取指令 n + 2 超标量 CPU 两个或更多的指令被同时取出、解码并装入暂存缓冲区中,直至它们执行完毕.只要有一个执行单元空闲.就检查保持缓冲区中是否还有可处理的指令,如果有,就把指令从缓冲区中移出并执行之. 两种模式 内核态 用户态 系统调用 为了从操作系统中获得服务,用户程序必须使用系统调用(system call)以陷入内核井调用操作系统。TRAP 指令把用户态切换成内核态,并启用操作系统。 多线程和多核芯片 多线程允许 CPU 保持两个不同的线程状态,然后在纳秒级的时间尺度内来回切换。 多线程不提供其正的并行处理。在一个时刻只有一个进程在运行,但是线程的切换时间则减少到纳秒数量级。 存储器 寄存器,纳秒数量级时间 高速缓存 个位数纳秒数量级时间 高速缓存命中 多级缓存 不同的缓存位置的影响 主存 易失性与非易失性存储介质 十位数纳秒数量级时间 闪存 电可擦除可编程内存 CMOS 存储器 磁盘 毫秒数量级时间 磁道 柱面 固态硬盘 虚拟内存机制 MMU 上下文切换 I/O 设备 设备控制器 插在电路板上的一块芯片或一组芯片 这块电路板物理地控制设备。它从操作系统接收命令 控制器的任务是为操作系统提供一个简单的接口 读写过程很复杂,控制器中经常安装一个小的嵌入式计算机,该嵌入式计算机运行为执行这些工作而专门编好的程序。 设备本身 本身有个相对简单的标准化接口 SATA 串行高级技术附件(Serial Advanced Technology Attachment) 由于实际的设备接口隐藏在控制器中,所以,操作系统看到的是对控制器的接口 设备驱动程序 将内核与设备驱动程序重新链接,然后重启动系统。 在一个操作系统文件中设置一个入口,并通知该文件需要一个设备驱动程序,然后重新启动系统。在系统启动时,操作系统去找寻所需的设备驱动程序井装载之。 操作系统能够在运行时接受新的设备驱动程序并且立即将其安装好,无须重启动系统。 每个设备控制器都有少址用千通信的寄存器 一个最小的磁盘控制器也会有用干指定磁盘地址、内存地址、扇区计数和方向(读或写)的寄存器。 要激活控制器,设备驱动程序从操作系统获得一条命令,然后翻译成对应的值,并写进设备寄存器中。所有设备寄存器的集合构成了 I/O 端口空间, 通信方式 在有些计算机中,设备寄存器被映射到操作系统的地址空间,不需要专门的 I/O 指令,用户程序可以被硬件阻挡在外,防止其接触这些存储器地址 另一些机器中,设备寄存器被放入一个专门的 I/O 端口空间中,每个寄存器都有一个端口地址。在这些机器中,提供在内核态中可使用的专门 IN 和 OUT 指令,供设备驱动程序读写这些寄存器用。 实现输入输出的方式 用户程序发出一个系统调用,内核将其翻译成一个对应设备驱动程序的过程调用。然后设备驱动程序启动 I/O 并在一个连续不断的循环中检查该设备,看该设备是否完成了工作(一般有一些二进制位用来指示设备仍在忙碌中)。当 I/O 结束后,设备驱动程序把数据送到指定的地方(若有此需要),井返回。然后操作系统将控制返回给调用者。这种方式称为忙等待(busywaiting), 其缺点是要占据 CPU, CPU 一直轮询设备直到对应的 I/O 操作完成。 设备驱动程序启动设备并且让该设备在操作完成时发出一个中断。设备驱动程序在这个时刻返回。操作系统接若在需要时阻塞调用者井安排其他工作进行。当设备驱动程序检查到该设备的操作完毕时,它发出一个中断通知操作完成。 为 I/O 使用一种特殊的直接存储器访问 (Direct Memory Access, DMA) 芯片,它可以控制在内存和某些控制器之间的位流,而无须持续的 CPU 干预。 CPU 对 DMA 芯片进行设置,说明需要传送的字节数、有关的设备和内存地址以及操作方向,接着启动 DMA。当 DMA 芯片完成时,它引发一个中断,其处理方式如前所述。 总线 主板上有很多不同作用的总线 PCIe 总线是 PCI 总线的继承者,PCI 总线是为了取代原来的 ISA 总线,速度快 USB,将所有慢速 I/O 设备与计算机连接,是一种集中式总线 SCSI,是一种高速总线,用在高速硬盘、扫描仪和其他需要较大带宽的设备上。 启动计算机 主板上有一块称为 BIOS 的程序,存储在非易失性存储介质中 主板加电,BIOS 开始运行,检查设备 根据 CMOS 存储的设备清单启动设备,从启动分区读入操作系统,启动它 操作系统访问 BIOS,获取设备信息,加载设备驱动程序,初始化相关程序。 操作系统大观园 大型机操作系统 用于大型机的操作系统主要面向多个作业的同时处理,多数这样的作业需要巨大的 I/O 能力。系统主要提供三类服务:批处理、事务处理和分时。 服务器操作系统 它们在服务器上运行,服务器可以是大型的个人计算机、工作站,甚至是大型机。它们通过网络同时为若干个用户服务,并且允许用户共享硬件和软件资源。 多处理器操作系统 获得大量联合计算能力的常用方式是将多个 CPU 连接成单个的系统。依据连接和共享方式的不同,这些系统称为并行计算机、多计算机或多处理器。它们需要专门的操作系统,不过通常采用的操作系统是配有通信、连接和一致性等专门功能的服务器操作系统的变体。 个人计算机操作系统 现代个人计算机操作系统都支持多道程序处理,在启动时,通常有几十个程序开始运行 。它们的功能是为单个用户提供良好的支持。 掌上计算机操作系统 大多数设备基于的是多核 CPU、GPS、摄像头及其他的传感器、大量内存和精密的操作系统。 嵌入式操作系统 嵌入式系统在用来控制设备的计算机中运行,这种设备不是一般意义上的计算机,井且不允许用户安装软件 。 传感器节点操作系统 每个传感器节点是一个配有 CPU、RAM、ROM 以及一个或多个环境传感器的实实在在的计算机。节点上运行一个小型但是真实的操作系统,通常这个操作系统是事件驱动的,可以响应外部事件,或者基于内部时钟进行周期性的测量。 实时操作系统 这些系统的特征是将时间作为关键参数。工控系统,民航,军事用途。硬实时操作系统。 多媒体,数字音频,软实时操作系统,允许一定延迟。 智能卡操作系统 智能卡是一种包含一块 CPU 芯片的信用卡。它有非常严格的运行能耗和存储空间的限制。其中,有些智能卡只具有单项功能,如电子支付,但是其他的智能卡则拥有多项功能.它们有专用的操作系统. 操作系统概念 进程 进程本质上是正在执行的一个程序 每个进程拥有自己的地址空间 进程表 进程间通信 每个进程都归属于某个用户 地址空间 通常,每个进程有一些可以使用的地址集合,典型值从 0 开始直到某个最大值。 虚拟内存,操作系统可以把部分地址空间装入主存,部分留在磁盘上,并且在需要时来回交换它们。在本质上,操作系统创建了一个地址空间的抽象,作为进程可以引用地址的集合。该地址空间与机器的物理内存解耦,可能大于也可能小干该物理空间。对地址空间和物理空间的管理组成了操作系统功能的一个重要部分。 文件 为磁盘和其他 I/O 设备提供一个良好的抽象文件模型。 目录和文件产生的层次结构,文件系统。 每个进程都有一个工作目录 每个打开的文件都有一个文件描述符。 挂载磁盘文件系统到根文件系统上 特殊文件,提供特殊文件是为了使 I/O 设备看起来像文件一般,块特殊文件,磁盘,字符特殊文件,字符流设备 管道,虚文件,它可以连接两个进程 输入输出 操作系统有各种类型的输人和输出设备,包括键盘、显示器、打印机等。对这些设备的管理全然依靠操作系统。 每个操作系统都有管理其 I/O 设备的 I/O 子系统。 保护 rwx 文件权限 管理员,用户组,普通用户管理 shell 命令解释器 并不是操作系统的一部分,提供了终端用户于操作系统之间的接口 个体重复系统发育 技术的变化会导致某些思想过时并迅速消失,这种情形经常发生。但是,技术的另一种变化还可能使某些思想再次复活.在技术的变化影响了某个系统不同部分之间的相对性能时,情况就是这样。 系统调用 操作系统具有两种功能:为用户程序提供抽象和管理计算机资源。 进行系统调用就像进行一个特殊的过程调用,但是只有系统调用可以进人内核,而过程调用则不能。 read 系统调用过程 调用程序首先把参数压进堆栈 对库过程的实际调用 把系统调用的编号放到寄存器中 执行 TRAP 指令,将用户态切换到内核态,并在内核中的一个固定地址开始执行 TRAP 指令后的内核代码开始检查系统调用编号,然后分派给正确的系统调用处理器,这通常是通过一张由系统调用编号所引用的、指向系统调用处理器的指针表来完成 系统调用处理器运行 一且系统调用处理器完成其工作,控制可能会在跟随 TRAP 指令后面的指令中返回给用户空间库过程 接着以通常的过程调用返回的方式,返回到用户程序 清除堆栈 常用系统调用 进程管理: fork waitpid execve exit 文件管理: open close read write lseek stat 目录和文件系统管理: mkdir rmdir link unlink mount umount 各种系统调用: chdir chmod kill time 操作系统结构 单体系统 整个操作系统以过程集合的方式编写,链接成一个大型可执行二进制程序 每个过程都可以自由调用其他过程 容错性差 系统笨拙难以理解 层次式系统 层次式结构的操作系统,它的上层软件都是在下一层软件的基础之上构建的 该系统的各个部分最终仍然被链接成了完整的单个目标程序 微内核 为了实现高可靠性,将操作系统划分成小的、良好定义的模块,只有其中一个模块一一微内核一运行在内核态,其余的模块由于功能相对弱些,则作为普通用户进程运行。 客户端一服务器模式 一般来说,客户端和服务器之间的通信是消息传递。为了获得一个服务,客户端进程构造一段消息,说明所需要的服务,井将其发给合适的服务器。该服务器完成工作,发送回应。 客户端和服务器运行在不同的计算机上,它们通过局域网或广域网连接 虚拟机 虚拟机监控程序 共享托管 第一类虚拟机管理程序 模拟器 虚拟化 二进制翻译 Java 虚拟机 外核 对机器进行分区 为虚拟机分配资源,井检查使用这些资源的企图,以确保没有机器会使用他人的资源 依靠 C 的世界 指针 头文件 目标文件 C 预处理器 链接 运行模型 有关操作系统的研究 错误调试 故障恢复 文件和存储系统 等等等

2021 Jul 14 · 2 min

计算机网络自顶向下方法::计算机网络中的安全

什么是网络中的安全 机密性 (confidentiality)。仅有发送方和希望的接收方能够理解传输报文的内容因为窃听者可以截获报文,这必须要求报文在一定程度上进行加密(encrypted),使截取的报文无法被截获者所理解。机密性的这个方面大概就是通常意义上对于术语安全通信的理解。 报文完整性 (message integrity)。Alice 和 Bob 希望确保其通信的内容在传输过程中未被改变——或者恶意篡改或者意外改动。我们在可靠传输和数据链路协议中遇到的检验和技术在扩展后能够用于提供这种报文完整性。 端点鉴别(end-point authentication)。发送方和接收方都应该能证实通信过程所涉及的另一方,以确信通信的另一方确实具有其所声称的身份。人类的面对面通信可以通过视觉识别轻易地解决这个问题。当通信实体在不能看到对方的媒体上交换报文时,鉴别就不是那么简单了。当某用户要访问一个邮箱时,邮件服务器如何证实该用户就是他所声称的那个人呢。 运行安全性(operational security)。几乎所有的机构(公司、大学等)今天都有了与公共因特网相连接的网络。这些网络都因此潜在地能够被危及安全。攻击者能够试图在网络主机中安放蠕虫,获取公司秘密,勘察内部网络配置并发起 DoS 攻击。我们将看到诸如防火墙和入侵检测系统等运行设备正被用于反制对机构网络的攻击。防火墙位于机构网络和公共网络之间,控制接入和来自网络的分组。入侵检测系统执行“深度分组检查“任务,向网络管理员发出有关可疑活动的警告。 密码学的原则 对称密钥密码体制 历史上的单密码 凯撒密码 单密码替换 多码代替密码 块密码 该密码用在多种因特网协议的加密中,包括 PGP (用于安全电子邮件)、SSL(用于使 TCP 连接更安全)和 IPsec(用于使网络层传输更安全)。 在块密码中,要加密的报文被处理为 K 比特的块。每块被独立加密。为了加密一个块,该密码采用了一对一映射,将 K 比特块的明文映射为 K 比特块的密文。 目前有一些流行的块密码,包括 DES (Data Encryption Standard, 数据加密标准)3DES 和 AES (Advanced Encryption Standard, 高级加密标准)。 密码块链接 在计算机网络应用中,通常需要加密长报文(或长数据流)。如果使用前面描述的块密码,通过直接将报文切割成 K 比特块并独立地加密每块,将出现一个微妙而重要的问题。为了理解这个问题,注意到两个或更多个明文块可能是相同的。例如,两个或更多块中的明文可能是 HHTTP/1.1 。对于这些相同的块,块密码当然将产生相同的密文。当攻击者看到相同的密文块时,它可能潜在地猜出其明文,并且通过识别相同的密文块和利用支撑协议结构的知识,甚至能够解密整个报文。为了解决这个问题,可以在密文中混合某些随机性,使得相同的明文块产生不同的密文块。 公开密钥加密 公开密钥密码学(英语:Public-key cryptography)也称非对称式密码学(英语:Asymmetric cryptography)是密码学的一种算法,它需要两个密钥,一个是公开密钥,另一个是私有密钥;公钥用作加密,私钥则用作解密。使用公钥把明文加密后所得的密文,只能用相对应的私钥才能解密并得到原本的明文,最初用来加密的公钥不能用作解密。由于加密和解密需要两个不同的密钥,故被称为非对称加密;不同于加密和解密都使用同一个密钥的对称加密。公钥可以公开,可任意向外发布;私钥不可以公开,必须由用户自行严格秘密保管,绝不透过任何途径向任何人提供,也不会透露给被信任的要通信的另一方。 RSA 报文完整性和数字签名 密码散列函数 MD5,SHA-1,SHA-256 报文鉴别码 发送发送方使用一个密钥和特定算法对明文产生一个短小的定长数据分组,即 MAC(报文鉴别码),并将它附加在报文中。在接收方,使用相同密钥的和算法对明文计算 MAC,如果新的 MAC 与报文中的 MAC 匹配,那么接受者确信报文未被修改过,接受者确信报文来自所期望的发送方。 数字签名 在数字领域,人们通常需要指出一个文件的所有者或创作者,或者表明某人认可一个文件内容。数字签名(digital signature) 就是一种在数字领域实现这些目标的密码技术。 Bob 让他的初始长报文通过一个散列函数。然后他用自己的私钥对得到的散列进行数字签名。明文形式的初始报文连同已经数字签名的报文摘要(从此以后可称为数字签名)一道被发送给 Alice。 Alice 先把发送方的公钥应用于报文获得一个散列结果。然后她再把该散列函数应用于明文报文以得到第二个散列结果。如果这两个散列匹配,则 Alice 可以确信报文的完整性及其发送方 。 公钥认证 数字签名的一个重要应用是公钥认证 (public key certification),即证实一个公钥属于某个特定的实体。公钥认证用在许多流行的安全网络协议中,包括 IPsec 和 SSL。 CA 证实一个实体( 一个人 、一台路由器等)的真实身份。如何进行认证并没有强制的过程。当与一个 CA 打交道时,一方必须信任这个 CA 能够执行适当的严格身份验证。 一旦 CA 验证了某个实体的身份,这个 CA 会生成一个将其身份和实体的公钥绑定起来的证书(certificate)。这个证书包含这个公钥和公钥所有者全局唯一的身份标识信息(例如,一个人的名字或一个 IP 地址)。由 CA 对这个证书进行数字签名。 端点鉴别 当经网络进行鉴别时,通信各方不能依靠生物信息比如外表、声波纹等进行身份鉴别。的确,我们会在后面的实例研究中看到,诸如路由器、客户/服务器进程等网络元素通常必须相互鉴别。此处,鉴别应当在报文和数据交换的基础上,作为某鉴别协议 (authentication protocol)的一部分独立完成。鉴别协议通常在两个通信实体运行其他协议(例如,可靠数据传输协议、路由选择信息交换协议或电子邮件协议)之前运行。鉴别协议首先建立相互满意的各方的标识;仅当鉴别完成之后,各方才继续下面的工作。 不重数和对称密钥密码体制 Alice 向 Bob 发送报文“我是 Alice" 。 Bob 选择一个不重数 R , 然后把这个值发送给 Alice 。 Alice 使用她与 Bob 共享的对称秘密密钥 \(K_{A-B}\) 来加密这个不重数,然后把加密的不 重数 \(K_{A-B}(R)\) 发回给 Bob。与在协议 ap3.1 中一样,由于 Alice 知道 \(K_{A-B}\) 并用它加密一个值,就使得 Bob 知道收到的报文是由 Alice 产生的。这个不重数用于确定 Alice 是活跃的 。 Bob 解密接收到的报文。如果解密得到的不重数等于他发送给 Alice 的那个不重数,则可鉴别 Alice 的身份 。 安全电子邮件 Alice 发送邮件时 Alice 对她要发送的报文 m 应用一个散列函数 H(例如 MD5), 从而得到一个报文摘要; 用她的私钥 SK 对散列函数的结果进行签名,从而得到一个数字签名; 把初始报文(未加密)和该数字签名级联起来生成一个包; 向 Bob 的电子邮件地址发送这个包。 当 Bob 接收到这个包时 他将 Alice 的公钥 PBK 应用到被签名的报文摘要上; 将该操作的结果与他自己对该报的散列 H 进行比较 PGP 安装 PGP 时,软件为用户产生一个公开密钥对。该公钥能被张贴到用户的网站上或放置在某台公钥服务器上。 私钥使用用户口令保护。 PGP 允许用户选择是否对报文加密,数字签名,并对数字签名加密。 PGP 也提供了一种公钥认证机制,但是这种机制与更为传统的 CA 差异很大。PGP 公钥由一个可信 Web 验证。当 Alice 相信 一个密钥/用户名对确实匹配时,她自己就可以验证这一密钥/用户名对。 使 TCP 连接安全:SSL TCP 的这种强化版本通常被称为安全套接字层(Secure Socket Layer, 修改的版本被称为运输层安全性(Transport Layer Secu1ily, SSL)。SSL 版本 3 的一个稍加修改的版本被称为运输层安全性(Transport Layer Securily,TLS), 已经由 IETF 标准化。 握手 Bob 与 Alice 创建一条 TCP 连接 验证 Alice 是真实的 Alice 发送给 Alice 一个主密钥,Bob 和 Alice 待用该主密钥生成 SSL 会话所需的所有对称密钥 一旦创建了 TCP 连接, Bob 就向 Alice 发送一个 hello 报文 。Alice 则用她的证书进行响应,证书中包含了她的公钥。因为该证书已被某 CA 证实过,Bob 明白无误地知道该公钥属于 Alice。 Bob 产生一个主密钥 (MS)(该 MS 将仅用于这个 SSL 会话),用 Alice 的公钥加密该 MS 以生成加密的主密钥 (EMS),并将该 EMS 发送给 Alice Alice 用她的私钥解密该 EMS 从而得到该 MS SSL 握手 客户发送它支持的密码算法的列表,连同一个客户的不重数。 从该列表中,服务器选择一种对称算法(例如 AES)、一 种公钥算法(例如具有特定密钥长度的 RSA)和一种 MAC 算法。它把它的选择以及证书和一个服务器不重数返回给客户。 客户验证该证书,提取服务器的公钥,生成一个前主密钥(Pre-Master Secret,PMS),用服务器的公钥加密该 PMS, 并将加密的 PMS 发送给服务器 。 使用相同的密钥导出函数(就像 SSL 标准定义的那样),客户和服务器独立地从 PMS 和不重数中计算出主密钥(Master Secret,MS)。然后该 MS 被切片以生成两个密码和两个 MAC 密钥。此外,当选择的对称密码应用于 CBC(例如 3DES 或 AES),则两个初始化向量(Initialization Vector,IV)也从该 MS 获得,这两个 IV 分别用于该连接的两端。自此以后,客户和服务器之间发送的所有报文均被加密和鉴别(使用 MAC)。 客户发送所有握手报文的一个 MAC 。 服务器发送所有握手报文的一个 MAC 。 关闭连接 在某个时刻,Bob 或者 Alice 将要终止 SSL 会话。一个方法是让 Bob 通过直接终止底层的 TCP 连接来结束该 SSL 会话,这就是说,通过让 Bob 向 Alice 发送一个 TCP FIN 报文段。但是这种幼稚设计为截断攻击(truncation attack)创造了条件,Trudy 再一次介入一个进行中的 SSL 会话中,并用 TCP FIN 过早地结束了该会话。如果 Trudy 这样做的话,Alice 将会认为她收到了 Bob 的所有数据,而实际上她仅收到了其中的一部分。对这个问题的解决方法是,在类型字段中指出该记录是否是用于终止该 SSL 会话的。(尽管 SSL 类型是以明文形式发送的,但在接收方使用了记录的 MAC 对它进行了鉴别。)通过包括这样一个字段,如果 Alice 在收到一个关闭 SSL 记录之前突然收到了一个 TCP FIN,她可能知道正在进行着某些耍花招的事情。 网络层安全性:IPsec 和虚拟安全网 IP 安全(IP Security)协议更常被称为 IPsec,它为网络层提供了安全性。IPsec 为任意两个网络层实体(包括主机和路由器)之间的 IP 数据报提供了安全。如我们很快要描述的那样,许多机构(公司、政府部门、非营利组织等等)使用 IPsec 创建了运行在公共因特网之上的虚拟专用网 (Vir1ual Private, Network, VPN) 。 除了机密性,网络层安全协议潜在地能够提供其他安全性服务。例如,它能提供源鉴别,使得接收实体能够验证安全数据报的源。网络层安全协议能够提供数据完整性,使得接收实体能够核对在数据报传输过程中可能出现的任何篡改。网络层安全服务也能提供防止重放攻击功能,这意味着 Bob 能够检测任何攻击者可能插入的任何冗余数据报。我们将很快看到 IPsec 的确提供了用于些安全服务的机制,即机密性、源鉴别、数据完整性和重放攻击防护。 IPsec 和虚拟专用网 当总部中的一台主机向某旅馆中的某销售员发送一个 IP 数据报时,总部中的网关路由器将经典的 IPv4 转换成为 IPsec 数据报,然后将该 IPsec 数据报转发进因特网。该 IPsec 数据报实际上具有传统的 IPv4 首部,因此在公共因特网中的路由器处理该数据报,仿佛它对路由器而言是一个普通的 IPv4 数据报。IPsec 数据报的载荷包括了一个 IPsec 首部,该首部被用于 IPsec 处理;此外,IPsec 数据报的载荷是被加密的。当该 IPsec 数据报到达销售员的便携机时,便携机的操作系统解密载荷并提供其他安全服务,如验证数据完整性,并将解密的载荷传递给上层协议(例如,给 TCP 或 UDP)。 运行安全性:防火墙和入侵检测系统 防火墙(firewall)是一个硬件和软件的结合体,它将一个机构的内部网络与整个因特网隔离开,允许一些数据分组通过而阻止另一些分组通过。防火墙允许网络管理员控制外部和被管理网络内部资源之间的访问,这种控制是通过管理流人和流出这些资源的流量实现的。 从外部到内部和从内部到外部的所有流量都通过防火墙。 仅被授权的流量(由本地安全策略定义)允许通过。 防火墙自身免于渗透。 防火墙分类 传统分组过滤器 (traditional packet filter) 一个机构通常都有一个将其内部网络与其 ISP(并因此与更大的公共因特网相连)相连的网关路由器。所有离开和进入内部网络的流量都要经过这个路由器,而这个路由器正是分组过滤(packet filtering)出现的地方。分组过滤器独立地检查每个数据报,然后基于管理员特定的规则决定该数据报应当允许通过还是应当丢弃。 状态过滤器 (slateful filter) 传统的防火墙无法管理连接,大量的连接仍然能使内部网络崩溃。状态过滤器通过用一张连接表来跟踪所有进行中的 TCP 连接来解决这个问题。防火墙能够通过观察三次握手(SYN、SYN ACK 和 ACK)来观察一条新连接的开始;而且当它看到该连接的一个 FIN 分组时,它能够观察该连接的结束,当防火墙经过比如说 60 秒还没有看到该连接的任何活动性,它也能够(保守地)假设该连接结束了。 应用程序网关 为了得到更高水平的安全性,防火墙必须把分组过滤器和应用程序网关结合起来。应 用程序网关除了看 IP/TCP/UDP 首部外,还基于应用数据来做策略决定。一个应用程序网关 (application gateway )是一个应用程序特定的服务器,所有应用程序数据(入和出的)都必须通过它。多个应用程序网关可以在同一主机上运行,但是每一个网关都是有自己的进程的单独服务器 。 入侵检测程序 为了检测多种攻击类型,我们需要执行深度分组检查(deep packet inspection),即查看首部字段以外部分,深入查看分组携带的实际应用数据。应用程序网关经常做深度分组检查。而一个应用程序网关仅对一种特定的应用程序执行这种检查。 入侵检测设备仅能够检查所有通过它传递的分组的首部(类似于分组过滤器 ),而且能执行深度分组检查(与分组过滤器不同)的设备。当这样的设备观察到一个可疑的分组时,或一系列可疑的分组时,它能够防止这些分组进入该机构网络。或者仅仅是因为觉得该活动可疑,该设备虽说能够让这些分组通过,但要向网络管理员发出告警,网络管理员然后密切关注该流量并采取适当的行动。 当观察到潜在恶意流量时能产生告警的设备称为入侵检测系统 (Intrusion Detection System,IDS)。滤除可疑流量的设备称为入侵防止系统(Intrusion Prevention System , IPS) 。

2021 Jun 19 · 3 min

计算机网络自顶向下方法::链路层与局域网

链路层概述 在本章中为方便讨论,将运行链路层协议协议的任何设备均称为节点。节点包括主机、路由器、交换机和 WiFi 接入点 。我们也把沿着通信路径连接相邻节点的通信信道称为链路。为了将一个数据报从源主机传输到目的主机,数据报必须通过沿端到端路径上的各段链路传输。 链路层提供的服务 成帧(framing).在每个网络层数据报经链路传送之前,几乎所有的链路层协议都要将其用链路层帧封装起来。一个帧由一个数据字段和若干首部字段组成,其中网络层数据报就插在数据字段中。帧的结构由链路层协议规定。 链路接入。媒体访问控制(Medium Access Control, MAC)协议规定了帧在链路上传输的规则。 可靠交付。当链路层协议提供可靠交付服务时。它保证无差错地经链路层移动每个网络层数据报 差错检测和纠正。当帧中的一个比特作为 1 传输时,接收方节点中的链路层硬件可能不正确地将其判断为 0,反之亦然。这种比特差错是由信号衰减和电磁噪声导致的。因为没有必要转发一个有差错的数据报,所以许多链路层协议提供一种机制来检测这样的比特差错。通过让发送节点在帧中包括差错检测比特,让接收节点进行差错检查,以此来完成这项工作。 链路层在何处实现 链路层的主体部分是在网络适配器(network adapter)中实现的,网络适配器有时也称为网络接口卡(Nehvork Interface Card, NIC)。位于网络适配器核心的是链路层控制器,该控制器通常是一个实现了许多链路层服务(成帧、链路接入、差错检测等)的专用片。因此,链路层控制器的许多功能是用硬件实现的。 在发送端,控制器取得了由协议栈较高层生成并存储在主机内存中的数据报,在链路层帧中封装该数据报(填写该帧的各个字段),然后遵循链路接入协议将该帧传进通信链路中。在接收端,控制器接收了整个帧,抽取出网络层数据报。如果链路层执行差错检 测,则需要发送控制器在该帧的首部设置差错检测比特,由接收控制器执行差错检测。 链路层的软件组件实现了高层链路层功能,如组装链路层寻址信息和激活控制器硬件。在接收端,链路层软件响应控制器中断,处理差错条件和将数据报向上传递给网络层。所以,链路层是硬件和软件的结合体。 差错检测和纠正技术 奇偶位校验 假设要发送的信息 D 有 d 比特。在偶校验方案中,发送方只需包含一个附加的比特,选择它的值,使得这 d+1 比特中 1 的总数是偶数。对于奇校验方案,选择校验比特值使得有奇数个 1。单个校验比特被存放在一个单独的字段中。 采用单个奇偶校验位方式。接收方的操作也很简单。接收方只需要数一数接收的 d + 1 比特中 1 的数目即可。如果在采用偶校验方案中发现了奇数个值为 1 的比特,接收方知道至少出现了一个比特差错。更精确的说法是,出现了奇数个比特差错。 二维奇偶校验 单比特奇偶校验方案的二维一般化方案。这里 D 中的 d 个比特被划分为 i 行 j 列。对每行和每列计算奇偶值。产生的 i+j+1 奇偶比特构成了链路层帧的差错检测比特。现在假设在初始 d 比特信息中出现了列单个比特差错。使用这种二维奇偶校验方案,包含比特值改变的列和行的校验值都将会出现差错。因此接收方不仅可以检测到出现了单个比特差错的事实,而且还可以利用存在奇偶校验差错的列和行的索引来实际识别发生差错的比特并纠正它! 接收方检测和纠正差错的能力被称为前向纠错,FEC 技术很有价值,因为它们可以减少所需的发送方重发的次数。也许更为重要的是,它们允许在接收方立即纠正差错。FEC 避免了不得不等待的往返时延,而这些时延是发送方收到 NAK 分组并向接收方重传分组所需要的,这对于实时网络应用或者具有长传播时延的链路(如深空间链路)可能是一种非常重要的优点。 检验和方法 因特网检验和(Internet checksum)就基于这种方法,即数据的字节作为 16 比特的整数对待并求和。这个和的反码形成了携带在报文段首部的因特网检验和。接收方通过对接收的数据(包括检验和)的和取反码,并且检测其结果是否为全 1 比特来检测检验和。如果这些比特中有任何比特是 0, 就可以指示出差错。检验和方法需要相对小的分组开销。例如,TCP 和 UDP 中的检验和只用了 16 比特。然而,与常用于链路层的 CRC 相比,它们提供相对弱的差错保护。 循环冗余检测 现今的计算机网络中广泛应用的差错检测技术基于循环冗余检测编码 CRC 编码也称为多项式编码,因为该编码能够将要发送的比特串看作为系数是 0 和 1 一个多项式,对比特串的操作被解释为多项式算术。 多路访问链路和协议 有两种类型的网络链路:点对点链路和广播链路。 点对点链路(point-to-point link)由链路一端的单个发送方和链路另一端的单个接收方组成。许多链路层协议都是为点对点链路设计的,如点对点协议 (point-to-point protocol, PPP)和高级数据链路控制(high-level data link control HDLC)就是两种这样的协议。 广播链路(broadcast link),它能够让多个发送和接收节点都连接到相同的、单一的、共享的广播信道上。广播信道通常用于局域网中,局域网是一个地理上集中在一座建筑物中的网络。 多路访问协议 信道划分协议 时分多路复用(TDM)和频分多路复用(FDM)是两种能够用于在所有共享信道节点之间划分广播信道带宽的技术。假设一个支持 N 个节点的信道且信道的传输速率为 Rbps。TDM 将时间划分为时间帧(time frame),并进一步划分每个时间帧为 N 个时隙(slot)。然后把每个时隙分配给 N 个节点中的一个。无论何时某个节点在有分组要发送的时候,它在循环的 TDM 帧中指派给它的时隙内传输分组比特。通常,选择的时隙长度应使一个时隙内能够传输单个分组。TDM 是有吸引力的,因为它消除了碰撞而且非常公平:每个节点在每个帧时间内得到了专用的传输速率 R/N bps。然而它有两个主要缺陷。首先,节点被限制于 R/N bps 的平均速率,即使当它是唯一有分组要发送的节点时。其次,节点必须总是等待它在传输序列中的轮次,即我们再次看到,即使它是唯一一个有帧要发送的节点。 FDM 将 R bps 信道划分为不同的频段(每个频段具有 R/N 带宽),并把每个频率分配给 N 个节点中的一个。因此 FDM 在单个较大的 R bps 信道中创建了 N 个较小的 R/N bps 信道。FDM 也有 TDM 同样的优点和缺点。它避免了碰撞.在 N 个节点之间公平地划分了带宽,然而,FDM 也有 TDM 所具有的主要缺点,也就是限制一个节点只能使用 R/N 的带宽,即使当它是唯一一个有分组要发送的节点时。 CDMA 对每个节点分配一种不同的编码。然后每个节点用它唯一的编码来对它发送的数据进行编码。如果精心选择这些编码,CDMA 网络具有一种奇妙的特性,即不同的节点能够同时传输,并且它们各自相应的接收方仍能正确接收发送方编码的数据比特,而不在乎其他节点的干扰传输 CDMA 已经在军用系统中使用了一段时间,目前已经广泛地用于民用,尤其是蜂窝电话中。 随机接入协议 在随机接入协议中,一个传输节点总是以信道的全部速率(即 R bps ) 进行发送,当有碰撞时,涉及碰撞的每个节点反复地重发它的帧(也就是分组),到该帧无碰撞地通过为止。但是当一个节点经历一次碰撞时,它不必立刻重发该帧。相反,它在重发该帧之前等待一个随机时延。涉及碰撞的每个节点独立地选择随机时延。因为该随机时延是独立地选择的,所以下述现象是有可能的:这些节点之一所选择的时延充分小于其他碰撞节点的时延,并因此能够无碰撞地将它的帧在信道中发出。 时隙 ALOHA 所有帧由 L 比特组成 时间被划分成长度为 L/R 秒的时隙。 节点只在时隙起点开始传输帧。 节点是同步的。每个节点都知道时隙何时开始。 如果在一个时隙中有两个或者更多个帧碰撞。则所有节点在该时隙结束之前检测到该碰撞事件。令 p 是一个概率,即一个在 0 和 1 之间的数。在每个节点中,时隙 ALOHA 的操作是简单的。 当节点有一个新帧要发送时,它等到下一个时隙开始并在该时隙传输整个帧。 如果没有碰撞,该节点成功地传输它的帧,从而不需要考虑重传该帧。 如果有碰撞,该节点在时隙结束之前检测到这次碰撞。每个时隙中重传它的帧,直到该帧被无碰撞地传输出去 时隙多路访问协议的效率定义为:当有大量的活跃节点且每个节点总有大量的帧要发送时,长期运行中成功时隙的份额。注意到如果不使用某种形式的访问控制,而且每个节点都在每次碰撞之后立即重传,这个效率将为零。时隙 ALOHA 显然增加了它的效率,使之大于零 载波侦听多路访问 (CSMA) 说话之前先听。如果其他人正在说话,等到他们说完话为止。在网络领域中,这被称为载波侦听(carrier sensing) 即一个节点在传输前先听信道。如果来自另一个节点的帧正向信道上发送,节点则等待直到检测到一小段时间没有传输,然后开始传输 。 如果与他人同时开始说话,停止说话。在网络领域中,这被称为碰撞检测(colliion detection),即当一个传输节点在传输时一直在侦听此信道户。如果它检测到另一个节点正在传输干扰帧,它就停止传输,在重复"侦听-当空闲时传输"循环之前等待一段随机时间。 这两个规则包含在载波侦听多路访问(Carrier Sense Multiple Access, CSMA) 和具有碰撞检测的 CSMA。 具有碰撞检测的载波侦听多路访问 (CSMA/CD) 适配器从网络层一条获得数据报,准备链路层帧,并将其放入帧适配器缓存中。 如果适配器侦听到信道空闲(即无信号能量从信道进入适配器),它开始传输帧。在另一方面,如果适配器侦听到信道正在忙,它将等待,直到侦听到没有信号能批时才开始传输帧。 在传输过程中,适配器监视来自其他使用该广播信道的适配器的信号能量的存在。 如果适配器传输整个帧而未检测到来自其他适配器的信号能量,该适配器就完成了该帧。在另一方面,如果适配器在传输时检测到来自其他适配器的信号能量,它中止传输(即它停止了传输帧)。 中止传输后,适配器等待一个随机时间量、然后返回步骤 2 轮流协议 轮询协议 轮询协议要求这些节点之一要被指定为主节点。主节点以循环的方式轮询(poll)每个节点。特别是,主节点首先向节点 1 发送一个报文,告诉它(节点 1)能够传输的帧的最多数量。在节点 1 传输了某些帧后,主节点告诉节点 2 它(节点 2)能够传输的帧的最多数量。上述过程以这种方式继续进行,主节点以循环的方式轮询了每个节点。轮询协议消除了困扰随机接入协议的碰撞和空时隙,这使得轮询取得高得多的效率。但是它也有一些缺点。第一个缺点是该协议引入了轮询时延,即通知一个节点”它可以传输”所需的时间。例如,如果只有一个节点是活跃的,那么这个节点将以小于 R bps 的速率传输,因为每次活跃节点发送了它最多数量的帧时,主节点必须依次轮询每一个非活跃的节点。第二个缺点可能更为严重,就是如果主节点有故障,整个信道都变得不可操作。 令牌传递协议 在这种协议中没有主节点。一个称为令牌(token)的小的特殊帧在节点之间以某种固定的次序进行交换。例如,节点 1 可能总是把令牌发送给节点 2,节点 2 可能总是把令牌发送给节点 3,而节点 N 可能总是把令牌发送给节点 1。当一个节点收到令牌时,仅当它有一些帧要发送时,它才持有这个令牌;否则,它立即向下一个节点转发该令牌。当一个节点收到令牌时,如果它确实有帧要传输,它发送最大数目的帧数,然后把令牌转发给下一个节点。令牌传递是分散的,并有很高的效率。但是它也有自己的一些问题。例如,一个节点的故障可能会使整个信道崩溃。或者如果一个节点偶然忘记了释放令牌,则必须调用某些恢复步骤使令牌返回到循环中来。 DOCSIS: 用于电缆因特网接入的链路层协议 一个电缆接入网通常在电缆网头端将几千个住宅电缆调制解调器与一个电缆调制解调器端接系统 CMTS 连接。CMTS 规范定义了电缆数据网络体系结构及其协议。DOCSIS 使用 FDM 将下行和上行(调制解调器到 CMTS) 网络段划分为多个频率信道。CMTS 在下行信道中传输的帧被所有在信道上做接收的电缆调制解调器接收到;然而因为仅有单一的 CMTS 在下行信道上传输,不存在多路访问问题。但在上行方向,存在着多个有趣的技术挑战,因为多个电缆调制解调器共享到 CMTS 的相同上行信道(频率),因此能够潜在地出现碰撞)。电缆调制解调器既不能侦听上行信道是否忙,也不能检测碰撞。相反,该电缆调制解调器如果没有在下一个下行控制报文中收到对请求分配的响应的话,就推断出它的微时隙请求帧经历了一次碰撞。当推断出一次碰撞,电缆调制解调器使用二进制指数回退将其微时隙请求帧延缓到以后的时隙重新发送。当在上行信道上有很少的流量,电缆调制解调器可能在名义上分配给微时隙请求帧的时隙内实际传输数据帧(因此避免不得不等待微时隙分配)。 交换局域网 链路层寻址和 ARP (ARP)该协议提供了将 IP 地址转换为链路层地址的机制。 MAC 地址 并不是主机或路由器具有链路层地址,而是它们的适配器(即网络接口)具有链路层地址。 链路层地址有各种不同的称呼:LAN 地址(LAN address)、物理地址(physical address)或 MAC 地址(MAC address)。因为 MAC 地址似乎是最为流行的术语,所以我们此后就将链路层地址称为 MAC 地址。 MAC 地址的一个有趣性质是没有两块适配器具有相同的地址。IEEE 在管理着该 MAC 地址空间。特别是,当一个公司要生产适配器时,它支付象征性的费用购买组成 2^24 个地址的一块地址空间。IEEE 分配这块 2^24 个地址的方式是:固定一个 MAC 地址的前 24 比特,让公司自已为每个适配器生成后 24 比特的唯一组合。 当某适配器要向某些目的适配器发送一个帧时、发送适配器将目的适配器的 MAC 地址插入到该帧中,并将该帧发送到局域网 上。 地址解析协议 因为存在网络层地址(例如,因特网的 IP 地址)和链路层地址(即 MAC 地址),所以需要在它们之间进行转换。对于因特网而言、这是地址解析协议(Address Resolution Protocol,ARP)的任务。 为了发送数据报,该源必须要向它的适配器不仅提供 IP 数据报,而且要提供目的主机 222.222.222.222 的 MAC 地址。然后发送适配器将构造一个包含目的地的 MAC 地址的链路层帧,并把该帧发送进局域网。 通过在发送主机中的 ARP 模块将取在相同局域网上的任何 IP 地址作为输入,然后返回相应的 MAC 地址获得 MAC 地址。 每台主机或路由器在其内存中具有一个 ARP 表(ARP table),这张表包含 IP 地址到 MAC 地址的映射关系该 ARP 表也包含一个寿命(TTL)值,它指示了从表中删除每个映射的时间。注意到这张表不必为该子网上的每台主机和路由器都包含一个表项;某些可能从来没有进入到该表中,某些可能已经过期。从一个表项放置到某 ARP 表中开始,一个表项通常的过期时间是 20 分钟。 当 ARP 表项不在时,发送方构造一个 ARP 分组,广播到连接在适配器上的所有设备。对应 MAC 地址的机器用标准帧回复。 最好把 ARP 看成是跨越链路层和网络层边界两边的协议 发送数据报到子网以外 子网内部通过 ARP 寻址发送数据包到路由器,子网之间的路由器也通过 ARP 协议获取 MAC 地址。 以太网 以太网因为布局早,价格,传输效率等原因已经占领绝大多数市场。 以太网帧结构 数据字段(46~1500 字节)。这个字段承载了 IP 数据报。以太网的最大传输单元(MTU)是 1500 字节。 目的地址(6 字节)。这个字段包含目的适配器的 MAC 地址,即 BB-BB-BB-BB-BB-BB。当适配器 B 收到一个以太网帧,帧的目的地址无论是 BB-BB-BB-BB-BB­-BB.还是 MAC 广播地址,它都将该帧的数据字段的内容传递给网络层;如果它收到了具有任何其他地址的帧,则丢弃之。 源地址(6 字节)。这个字段包含了传输该帧到局域网上的适配器的 MAC 地址 类型宇段(2 字节)。类型字段允许以太网复用多种网络层协议。 CRC 前同步码(8 字节)。以太网帧以一个 8 字节的前同步码(Preamble)字段开始。该前同步码的前 7 字节的值都是 10101010;最后一个字节是 10101011。前同步码字段的前 7 字节用于“唤醒“接收适配器,并且将它们的时钟和发送方的时钟同步。 以太网技术都向网络层提供不可靠服务。特别是,当适配器 B 收到一个来自适配器 A 的帧,它对该帧执行 CRC 校验,但是当该帧通过 CRC 校验时它既不发送确认帧;而当该帧没有通过 CRC 校验时它也不发送否定确认帧。当某帧没有通过 CRC 校验,适配器 B 只是丢弃该帧。 以太网技术 使用标准以太网帧格式, 并且后向兼容 1OBASE-T 与 1OOBASE-T 技术。这使得吉比特以太网和现已安装的以太网设备基础很容易集成。 允许点对点链路以及共享的广播信道。如前所述,点对点链路使用交换机,而广播信道使用集线器。在吉比特以太网术语中,集线器被称为"带缓存的分配器” 。 使用 CSMA/CD 来共享广播信道。为了得到可接受的效率,节点之间的最大距离必须严格限制。 对于点对点信道,允许在两个方向上都以 40 Gbps 全双工操作 。 通过所有这些改变,的确还有一个历经 30 年保持未变的持久不变量,即以太网帧格式。也许这才是以太网标准的一个真正重要的特征。 链路层交换机 交换机的任务是接收入链路层帧并将它们转发到出链路,交换机自身对子网中的主机和路由器是透明的,这就是说,某主机/路由器向另一个主机/路由器寻址一个帧,顺利地将该帧发送进局域网,并不知道某交换机将会接收该帧并将它转发到另一个节点。这些帧到达该交换机的任何输出接口之一的速率可能暂时会超过该接口的链路容量。为了解决这个问题,交换机输出接口设有缓存,这非常类似于路由器接口为数据报设有缓存。 交换机转发和过滤 过滤(filtering)是决定一个帧应该转发到某个接口还是应当将其丢弃的交换机功能。 转发(forwarcling)是决定一 个帧应该被导向哪个接口,并把该帧移动到那些接口的交换机功能。交换机的过滤和转发借助于交换机表(switch table)完成。该交换机表包含某局域网上某些主机和路由器的但不必是全部的表项。 假定目的地址为 DD-DD-DD-DD-DD-DD 的帧从交换机接口 x 到达。交换机用 MAC 地址 DD-DD-DD-DD-DD-DD 索引它的表。有 3 种可能的情况: 表中没有对于 DD-DD-DD-DD-DD-DD 的表项。在这种情况下,交换机向除接口 x 外的所有接口前面的输出缓存转发该帧的副本。换言之,如果没有对于目的地址的表项,交换机广播该帧。 表中有一个表项将 DD-DD-DD-DD-DD-DD 与接口 x 联系起来。在这种情况下,该帧从包括适配器 DD-DD-DD-DD-DD- DD 的局域网网段到来。无须将该帧转发到任何其他接口,交换机通过丢弃该帧执行过滤功能即可。 表中有一个表项将 DD-DD-DD-DD-DD-DD 与接口 y!=x 联系起来。在这种情况下,该帧需要被转发到与接口 y 相连的局域网网段。交换机通过将该帧放到接口 y 前面的输出缓存完成转发功能。 自学习 交换机表初始为空。 对于在每个接口接收到的每个入帧,该交换机在其表中存储:1.在该帧源地址字段中的 MAC 地址;2 该帧到达的接口;3 当前时间。交换机以这种方式在它的表中记录了发送节点所在的局域网网段。如果在局域网上的每个主机最终都发送了一个帧,则每个主机最终将在这张表中留有记录。 如果在一段时间(称为老化期(aging time))后,交换机没有接收到以该地址作为源地址的帧,就在表中删除这个地址。以这种方式,如果一台 PC 被另一台 PC (具有不同的适配器)代替,原来 PC 的 MAC 地址将最终从该交换机表中被清除掉。 交换机是即插即用设备(plug-and-play device),因为它们不需要网络管理员或用户的干预。要安装交换机的网络管理员除了将局域网网段与交换机的接口相连外,不需要做其他任何事。管理员在安装交换机或者当某主机从局域网网段之一被去除时,他没有必要配置交换机表。交换机也是双工的,这意味着任何交换机接口能够同时发送和接收。 链路层交换机的性质 消除碰撞。在使用交换机(不使用集线器)构建的局域网中,没有因碰撞而浪费的带宽!交换机缓存帧并且决不会在网段上同时传输多于一个帧。就像使用路由器一样,交换机的最大聚合带宽是该交换机所有接口速率之和。因此,交换机提供了比使用广播链路的局域网高得多的性能改善。 异质的链路。交换机将链路彼此隔离,因此局域网中的不同链路能够以不同的速率运行并且能够在不同的媒体上运行。 管理。除了提供强化的安全性交换机也易于进行网络管理。检测流量,检测问题发生。 交换机和路由器比较 交换机是第二层的分组交换机,而路由器是第三层的分组交换机 交换机是即插即用的,这是世界上所有超负荷工作的网络管理员都喜爱的特性。交换机还能够具有相对高的分组过滤和转发速率,交换机必须处理高至第二层的帧,而路由器必须处理高至第三层的数据报。在另一方面,为了防止广播帧的循环,交换网络的活跃拓扑限制为一棵生成树。另外,一个大型交换网络将要求在主机和路由器中有大的 ARP 表,这将生成可观的 ARP 流量和处理量。而且,交换机对于广播风暴并不提供任何保护措施,即如果某主机出了故障并传输出没完没了的以太网广播帧流,该交换机将转发所有这些帧使得整个以太网的崩溃。 因为网络寻址通常是分层次的(不像 MAC 寻址那样是扁平的),即使当网络中存在冗余路径时,分组通常也不会通过路由器循环。(然而,当路由器表被误配置时,分组可能循环;IP 用一个特殊的报文首部字段来限制循环。)所以,分组就不会被限制到一棵生成树上,并可以使用源和目的地之间的最佳路径。因为路由器没有生成树限制,所以它们允许以丰富的拓扑结构构建因特网,例如包括欧洲和北美之间的多条活跃链路。路由器的另一个特色是它们对第二层的广播风暴提供了防火墙保护。尽管也许路由器最重要的缺点就是它们不是即插即用的,即路由器和连接到它们的主机都需要人为地配置 IP 地址 C 而且路由器对每个分组的处理时间通常比交换机更长,因为它们必须处理高达第三层的字段。 通常,由几百台主机组成的小网络通常有几个局域网网段。对于这些小网络、交换机就足够了,因为它们不要求 IP 地址的任何配置就能使流量局部化并增加总计吞吐量。但是在由几千台主机组成的更大网络中,通常在网络中(除了交换机之外)还包括路由器。路由器提供了更健壮的流量隔离方式和对广播风暴的控制,并在网络的主机之间使用更智能的路由。 虚拟局域网 支持 VLAN 的交换机允许经一个单一的物理局域网基础设施定义多个虚拟局域网。在一个 VLAN 内的主机彼此通信,仿佛它们与交换机连接。在一个基于端口的 VLAN 中,交换机的端口(接口)由网络管理员划分为组。每个组构成一个 VLAN,在每个 VLAN 中的端口形成一个广播域。 链路虚拟化:网络化作链路层 多协议标签交换 与电路交换的电话网不同、MPLS 客观上讲是一种分组交换的虚电路网络。它们有自己的分组格式和转发行为。因此,从教学法 的观点看,有关 MPLS 的讨论既适合放在网络层的学习中、也适合放在链路层的学习中。然而,从因特网的观点看,我们能够认为 MPLS 像电话网和交换以太网一样,作为为 IP 设备提供互联服务的链路层技术。 数据中心网络 每个数据中心都容纳了数万至数十万台主机,并且同时支持着很多不同的云应用。数据中心都有自己的数据中心网络这些数据中心网络将其内部主机彼此互联并与因特网中的数据中心互联。 数据中心网络支持两种类型的流量:在外部客户与内部主机之间流动的流量,以及内部主机之间流动的流量。为了处理外部客户与内部主机之间流动的流量,数据中心网络包括了一台或者多台边界路由器(border router), 它们将数据中心网络与公共因特网相连 数据中心网络因此需要将所有机架彼此互联,并将机架与边界路巾器连接。 负载均衡 一个大型的数据中心通常会有几台负载均衡器,每台服务于一组特定的云应用。由于负载均衡器基于分组的目的端口号(第四层)以及目的 IP 地址做决策,因此它们常被称为“第四层交换机”。 等级体系结构 对于仅有数于台主机的小型数据中心,一个简单的网络也许就足够了。这种简单网络由一台边界路由器、一台负载均衡器和几十个机架组成,这些机架由单一以太网交换机进行互联。但是当主机规模扩展到几万至几十万的时候,数据中心通常应用路由器和交换机等级结构。 在该等级结构的顶端,边界路由器与接入路由器相连。在每台接入路由器下面,有 3 层交换机。每台接入路由器与一台第一层交换机相连,每台第一层交换机与多台第二层交换机以及一台负载均衡器相连。每台第二层交换机又通过机架的 TOR 交换机(第三层交换机)与多个机架相连。所有链路通常使用以太网作为链路层和物理层协议,并混合使用铜缆和光缆。通过这种等级式设计,可以将数据中心扩展到几十万台主机的规模。 数据中心网络的发展趋势 全连接拓扑 模块化数据中心 回顾:Web 页面请求的历程 一名学生 Bob 将他的便携机与学校的以太网交换机相连,下载一个 Web 页面(比如说 www.google.com主页)。 Bob 便携机上的操作系统生成一个 DHCP 请求报文,并将这个报文放入具有目的端口 67(DHCP 服务器)和源端口 68(DHCP 客户)的 UDP 报文段,该 UDP 报文段则被放置在一个具有广播 IP 目的地址(255.255.255.255)和源 IP 地址 0.0.0.0 的 IP 数据报中(4.3.I 节),因为 Bob 的便携机还没有一个 IP 地址。 包含 DHCP 请求报文的 IP 数据报则被放置在以太网帧中。该以太网帧具有目的 MAC 地址 FF:FF:FF:FF:FF:FF,使该帧将广播到与交换机连接的所有设备;该帧的源 MAC 地址是 Bob 便携机的 MAC 地址 00:I6:D3:23:68:8A。 包含 DHCP 请求的广播以太网帧是第一个由 Bob 便携机发送到以太网交换机的帧。该交换机在所有的出端口广播入帧,包括连接到路由器的端口。 路由器在它的具有 MAC 地址 00:22:6B:45:1F 的接口接收到该广播以太网帧,该帧中包含 DHCP 请求、并且从该以太网帧中抽取出 IP 数据报。该数据报的广播 IP 目的地址指示了这个 IP 数据报应当由在该节点的高层协议处理,因此该数据报的载荷(一个 UDP 报文段)被分解向上到达 UDP,DHCP 请求报文从此 UDP 报文段中抽取出来。此时 DHCP 服务器有了 DHCP 请求报文。 我们假设运行在路由器中的 DHCP 服务器能够以 CIDR 块 68.85.2.0/24 分配 IP 地址)所以本例中,在学校内使用的所有 IP 地址都在 Comcast 的地址块中。我们假设 DHCP 服务器分配地址 68.85.2.101 给 Bob 的便携机。DHCP 服务器生成包含这个 IP 地址以及 DNS 服务器的 IP 地址(68.87.71.226)、默认网关路由器的 IP 地址(68.85.2.l)。和子网块(68.85.2.0/24)(等价为“网络掩码")的一个 DHCP ACK 报文该 DHCP 报文被放入一个 UDP 报文段中,UDP 报文段被放人一个 IP 数据报中,IP 数据报再被放入一个以太网帧中。这个以太网帧的源 MAC 地址是路由器连到归属网络时接口的 MAC 地址址(00:22:6B:45:1F:1B),目的 MAC 地址是 Bob 便携机的 MAC 地址 包含 DHCP ACK 的以太网帧由路由器发送给交换机。因为交换机是自学习的、并且先前从 Bob 便携机收到(包含 DHCP 请求的)以太网帧,所以该交换机知道寻址到 00:16:03:23:68:8A 的帧仅从通向 Bob 便携机的输出端口转发。 Bob 便携机接收到包含 DHCPACK 的以太网帧,从该以太网帧中抽取 IP 数据报,从 IP 数据报中抽取 UDP 报文段,从 UDP 报文段抽取 DHCPACK 报文。Bob 的 DHCP 客户则记录下它的 IP 地址和它的 DNS 服务器的 IP 地址。它还在其 IP 转发表中安装默认网关的地址。Bob 便携机将向该默认网关发送目的地址为其子网 68.85.2.0/24 以外的所有数据报。此时,Bob 便携机已经初始化好它的网络组件,并准备开始处理 Web 网页获取。 Bob 便携机上的操作系统因此生成一个 DNS 查询报文,将字符串 www.google.com 放入 DNS 报文的问题段中。该 DNS 报文则放置在一个具有 53 号(DNS 服务器)目的端口的 UDP 报文段中。该 UDP 报文段则被放入具有 IP 目的地址 68.87.71.226 和源 IP 地址 68.85.2.101 的 IP 数据报中。 Bob 便携机则将包含 DNS 请求报文的数据报放入一个以太网帧中.该帧将发送(在链路层寻址)到 Bob 学校网络中的网关路由器。然而,即使 Bob 便携机经过上述第 5 步中的 DHCPACK 报文知道了学校网关路由器的 IP 地址(68.85.2.1),但仍不知道该网关路由器的 MAC 地址。为了获得该网关路由器的 MAC 地址,Bob 便携机将需要使用 ARP 协议。 Bob 便携机生成一个具有目的 lP 地址 68.85.2.1(默认网关)的 ARP 查询报文,将该 ARP 报文放置在一个具有广播目的地址(FF:FF:FF:FF:FF:FF)的以太网帧中,并向交换机发送该以太网帧,交换机将该帧交付给所有连接的设备,包括网关路由器。 网关路由器在通往学校网络的接口上接收到包含该 ARP 查询报文的帧,发现在 ARP 报文中目标 IP 地址 68.85.2.1 匹配其接口的 IP 地址。网关路由器因此准备一个 ARP 回答,指示它的 MAC 地址 00:22:6B:45:1F:1B 对应 IP 地址 68.85.2.1~它将 ARP 回答放在一个以太网帧中,其目的地址为 00:16:D3:23:68:8A(Bob 便携机),并向交换机发送该帧,再由交换机将帧交付给 Bob 便携机。 Bob 便携机接收包含 ARP 回答报文的帧并从 ARP 回答报文中抽取网关路由器的 MAC 地址(00:22:6B:45:1F:1B) Bob 便携机现在(最终!)能够使包含 DNS 查询的以太网帧寻址到网关路由器的 MAC 地址。注意到在该帧中的 IP 数据报具有 IP 目的地址 68.87.71.226(DNS 服务器),而该帧具有目的地址 00:22:68:45:1F:1B(网关路由器)。Bob 便携机向交换机发送该帧,交换机将该帧交付给网关路由器。 网关路由器接收该帧并抽取包含 DNS 查询的 IP 数据报。路由器查找该数据报的目的地址(68.87.71.226),并根据其转发表决定该数据报应当发送到 Comcast 网络中最左边的路由器 IP 数据报放置在链路层帧中,该链路适合将学校路由器连接到最左边 Comcast 路由器,并且该帧经这条链路发送。 在 Comcast 网络中最左边的路由器接收到该帧,抽取 IP 数据报,检查该数据报的目的地址(68.87.71.226),并根据其转发表确定出接口,经过该接口朝着 DNS 服务器转发数据报,而转发表己根据 Comcast 的域内协议(如 RIP、OSPF 或 IS-IS)以及因特网的域间协议 BGP 所填写。 最终包含 DNS 查询的 IP 数据报到达了 DNS 服务器。DNS 服务器抽取出 DNS 查询报文,在它的 DNS 数据库中查找名字www.google.com,找到包含对应www.google.com的IP地址(64.233.169.105)的DNS源记录,前面讲过这种缓存数据源于google.com的权威DNS服务器。该DNS服务器形成了一个包含这种主机名到IP地址映射的DNS回答报文,将该DNS回答报文放入UDP报文段中,该报文段放入寻址到Bob便携机(68.85.2.101)的IP数据报中。该数据报将通过Comcast网络反向转发到学校的路由器,并从这里经过以太网交换机到Bob便携机。 Bob 便携机从 DNS 报文抽取出服务器www.google.com的IP地址。最终,在大量工作后,Bob便携机此时准备接触www.google.com服务器! 既然 Bob 便携机有了www.google.com的IP地址,它能够生成TCP套接字(2.7节),该套接字将用于向吓叩.google.com发送HTTPGET报文(2.2.3节)。当Bob生成TCP套接字时,在Bob便携机中的TCP必须首先与www.google.com中的TCP执行三次握手。Bob 便携机因此首先生成一个具有目的端口 80(针对 HTTP 的)的 TCP SYN 报文段,将该 TCP 报文段放置在具有目的 1P 地址 64.233.169.105(www.google.com)的IP数据报中,将该数据报放置在MAC地址为00:22:68:45:1F:1B(网关路由器)的帧中,并向交换机发送该帧. 在学校网络、Comcast 网络和谷歌网络中的路由器朝着 www.google.com 转发包含 TCP SYN 的数据报,使用每台路由器中的转发表,如前面步骤 14~16 那样。前面讲过支配分组经 Comcast 和谷歌网络之间域间链路转发的路由器转发表项,是由 BGP 协议决定的 最终,包含 TCPSYN 的数据报到达www.googole.com0从数据报抽取出TCPSYN报文并分解到与端口80相联系的欢迎套接字。对于谷歌HTTP服务器和Bob便携机之间的TCP连接生成一个连接套接字。产生一个TCP SYN ACK 报文段,将其放入向 Bob 便携机寻址的一个数据报中,最后放入链路层帧中,该链路适合将www.google.com连接到其第一跳路由器。 包含 TCP SYN ACK 报文段的数据报通过谷歌、Comcast 和学校网络,最终到达 Bob 便携机的以太网卡,数据报在操作系统中分解到步骤 18 生成的 TCP 套接字,从而进入连接状态。 借助于 Bob 便携机上的套接字,现在(终于!)准备向www.googJe.com发送字节HTTP GET 报文则写入套接字,其中 GET 报文成为一个 TCP 报文段的载荷。该 TCP 报文段放置进一个数据报中,并交付到www.google.com,如前面步骤18-20所述。 在www.google.com的HTTP服务器从TCP套接字读取HTTP GET 报文,生成一个 HTTP 响应报文,将请求的 Web 页内容放入 HTTP 响应体中,并将报文发送了,Bob 的浏览器生成包含要获取的 URL 的 HTTPGET 报文进 TCP 套接字中。 包含 HTTP 回答报文的数据报通过谷歌、Comcast 和学校网络转发,到达 Bob 便携机。Bob 的 Web 浏览器程序从套接字读取 HTTP 响应,从 HTTP 响应体中抽取 Web 网页的 html, 并最终(终于!)显示了 Web 网页。

2021 Jun 18 · 5 min