操作系统导论

元数据

[!abstract] 操作系统导论

书名： 操作系统导论

作者： 雷姆兹・H. 阿帕希杜塞尔 安德莉亚・C. 阿帕希杜塞尔

简介： 这是一本关于现代操作系统的书。全书围绕虚拟化、并发和持久性这 3 个主要概念展开，介绍了所有现代系统的主要组件（包括调度、虚拟内存管理、磁盘和 I/O 子系统、文件系统 ）。 本书共 50 章，分为 3 个部分，分别讲述虚拟化、并发和持久性的相关内容。本书大部分章节均先提出特定的问题，然后通过书中介绍的技术、算法和思想来解决这些问题。笔者以对话形式引入所介绍的主题概念，行文诙谐幽默却又鞭辟入里，力求帮助读者理解操作系统中虚拟化、并发和持久性的原理。 本书内容全面，并给出了真实可运行的代码（而非伪代码），还提供了相应的练习，适合高等院校相关专业教师教学和高校学生自学。

出版时间 2019-06-01 00:00:00

ISBN： 9787115508232

分类： 计算机 - 计算机综合

出版社： 人民邮电出版社

高亮划线

前言

📌 虚拟化（virtualization）、并发（concurrency）和持久性（persistence）。 ^30179184-6-1240-1293

⏱ 2021-11-07 09:36:51

第 1 章 关于本书的对话

📌 教授：欢迎阅读这本书，本书英文书名为《Operating Systems Easy Pieces》，由我来讲授关于操作系统的知识。请做一下自我介绍。学生：教授，您好，我是学生，您可能已经猜到了，我已经准备好开始学习了！教授：很好。有问题吗？学生：有！本书为什么讲 “3 个简单部分”？教授：这很简单。理查德・费曼有几本关于物理学的讲义，非常不错…… 学生：啊，是《别闹了，费曼先生》的作者吗？那本书很棒！这书也会像那本书一样搞笑吗？教授：呃…… 不。那本书的确很棒，很高兴你读过它。我希望这本书更像他关于物理学的讲义。将一些基本内容汇集成一本书，名为《Six Easy Pieces》。他讲的是物理学，而我们将探讨的主题是操作系统的 3 个简单部分。这很合适，因为操作系统的难度差不多是物理学的一半。学生：懂了，我喜欢物理学。是哪 3 个部分呢？教授：虚拟化（virtualization）、并发（concurrency）和持久性（persistence）。这是我们要学习的 3 个关键概念。通过学习这 3 个概念，我们将理解操作系统是如何工作的，包括它如何决定接下来哪个程序使用 CPU，如何在虚拟内存系统中处理内存使用过载，虚拟机监控器如何工作，如何管理磁盘上的数据，还会讲一点如何构建在部分节点失败时仍能正常工作的分布式系统。学生：对于您说的这些，我都没有概念。教授：好极了，这说明你来对了地方。 ^30179184-6-359

⏱ 2021-10-13 09:10:43

📌 首先听课，听老师讲解并做好笔记，然后每个周末阅读笔记，以便更好地理解这些概念 ^30179184-6-1738-1776

⏱ 2021-11-07 10:17:48

📌 特别是做项目，你会编写真正的代码来解决真正的问题，这是将笔记中的概念活学活用。就像孔子说的那样…… ^30179184-6-1830-1879

⏱ 2021-11-07 10:17:55

📌 “不闻不若闻之，闻之不若见之，见之不若知之，知之不若行之。” ^30179184-6-1941-1971

⏱ 2021-11-07 10:18:11

📌 好问题！我觉得有的时候将自己从叙述中抽离出来，然后进行一些思考会更有用。这些对话就是思考。 ^30179184-6-2532-2577

⏱ 2021-11-07 10:19:27

第 2 章 操作系统介绍

📌 虚拟化让许多程序运行（从而共享 CPU），让许多程序可以同时访问自己的指令和数据（从而共享内存） ^30179184-7-2403-2450

⏱ 2021-10-13 09:11:38

📌 两个数相加、访问内存、检查条件、跳转到函数等） ^30179184-7-734-757

⏱ 2021-11-07 10:18:57

📌 操作系统通过哪些机制和策略来实现虚拟化？操作系统如何有效地实现虚拟化？需要哪些硬件支持？ ^30179184-7-1754-1798

⏱ 2021-11-07 10:19:11

📌 如果你正在读本科操作系统课程，那么应该已经初步了解了计算机程序运行时做的事情。如果不了解，这本书（和相应的课程）对你来说会很困难：你应该停止阅读本书，或跑到最近的书店，在继续读本书之前快速学习必要的背景知识（包括 Patt / Patel [PP03]，特别是 Bryant / O’Hallaron 的书 [BOH10]，都是相当不错的）。程序运行时会发生什么？一个正在运行的程序会做一件非常简单的事情：执行指令。处理器从内存中获取（fetch）一条指令，对其进行解码（decode）（弄清楚这是哪条指令），然后执行（execute）它（做它应该做的事情，如两个数相加、访问内存、检查条件、跳转到函数等）。完成这条指令后，处理器继续执行下一条指令，依此类推，直到程序最终完成 [插图]。这样，我们就描述了冯・诺依曼（Von Neumann）计算模型 [插图] 的基本概念。听起来很简单，对吧？但在这门课中，我们将了解到在一个程序运行的同时，还有很多其他疯狂的事情也在同步进行 —— 主要是为了让系统易于使用。实际上，有一类软件负责让程序运行变得容易（甚至允许你同时运行多个程序），允许程序共享内存，让程序能够与设备交互，以及其他类似的有趣的工作。这些软件称为操作系统（Operating System，OS）[插图]，因为它们负责确保系统既易于使用又正确高效地运行。关键问题：如何将资源虚拟化我们将在本书中回答一个核心问题：操作系统如何将资源虚拟化？这是关键问题。为什么操作系统这样做？这不是主要问题，因为答案应该很明显：它让系统更易于使用。因此，我们关注如何虚拟化：操作系统通过哪些机制和策略来实现虚拟化？操作系统如何有效地实现虚拟化？需要哪些硬件支持？ ^30179184-7-359

⏱ 2021-11-07 10:21:48

📌 执行指令 ^30179184-7-657-661

⏱ 2021-11-07 10:23:46

📌 获取（fetch）一条指令，对其进行解码（decode）（弄清楚这是哪条指令），然后执行（execute）它（做它应该做的事情，如 ^30179184-7-669-734

⏱ 2021-11-07 10:23:53

📌 如何将资源虚拟化 ^30179184-7-1626-1634

⏱ 2021-11-07 10:32:53

📌 尽管我们只有一个处理器，但这 4 个程序似乎在同时运行！这种魔法是如何发生的？￼ ^30179184-7-4049-4314

⏱ 2021-11-07 10:35:34

📌 将单个 CPU（或其中一小部分）转换为看似无限数量的 CPU，从而让许多程序看似同时运行，这就是所谓的虚拟化 CPU（virtualizing the CPU），这是本书第一大部分的关注点。 ^30179184-7-4399-4491

⏱ 2021-11-07 10:35:57

📌 如果两个程序想要在特定时间运行，应该运行哪个？这个问题由操作系统的策略（policy）来回答。 ^30179184-7-4680-4727

⏱ 2021-11-07 10:36:15

📌 操作系统承担了资源管理器（resource manager）的角色。 ^30179184-7-4812-4846

⏱ 2021-11-07 10:36:21

📌 读取（read）内存，必须指定一个地址（address），才能访问存储在那里的数据。要写入（write）或更新（update）内存，还必须指定要写入给定地址的数据。 ^30179184-7-4996-5078

⏱ 2021-11-07 10:50:35

📌 不要忘记，程序的每个指令都在内存中，因此每次读取指令都会访问内存。 ^30179184-7-5178-5211

⏱ 2021-11-07 10:50:51

📌 每个正在运行的程序都在相同的地址（00200000）处分配了内存，但每个似乎都独立更新了 00200000 处的值！就好像每个正在运行的程序都有自己的私有内存，而不是与其他正在运行的程序共享相同的物理内存￼。 ^30179184-7-6454-6723

⏱ 2021-11-07 10:54:54

📌 私有虚拟地址空间（virtual address space）（有时称为地址空间，address space），操作系统以某种方式映射到机器的物理内存 ^30179184-7-7116-7191

⏱ 2021-11-07 11:00:47

📌 物理内存是由操作系统管理的共享资源。 ^30179184-7-7261-7279

⏱ 2021-11-07 11:00:54

📌 程序利用 Pthread_create () 创建了两个线程（thread）￼。你可以将线程看作与其他函数在同一内存空间中运行的函数， ^30179184-7-8694-8907

⏱ 2021-11-07 11:01:38

📌 指令每次执行一条。遗憾的是，上面的程序中的关键部分是增加共享计数器的地方，它需要 3 条指令 ^30179184-7-10069-10113

⏱ 2021-11-07 11:03:54

📌 持久性需要哪些技术才能正确地实现？需要哪些机制和策略才能高性能地实现？面对硬件和软件故障，可靠性如何实现？ ^30179184-7-11947-12000

⏱ 2021-11-07 11:06:49

📌 open () 的调用，它打开文件并创建它。第二个是 write ()，将一些数据写入文件。第三个是 close () ^30179184-7-12068-12121

⏱ 2021-11-07 11:07:11

📌 硬件以某种输入 / 输出（Input/Output，I/O）设备的形式出现。 ^30179184-7-10527-10563

⏱ 2021-11-07 11:07:20

📌 大多数文件系统都包含某种复杂的写入协议，如日志（journaling）或写时复制（copy-on-write） ^30179184-7-12788-12843

⏱ 2021-11-07 11:08:52

📌 CPU、内存或磁盘等物理资源（resources），并对它们进行虚拟化（virtualize）。它处理与并发（concurrency）有关的麻烦且棘手的问题。它持久地（persistently）存储文件，从而使它们长期安全。 ^30179184-7-13143-13255

⏱ 2021-11-07 11:09:26

📌 它取得 ^30179184-7-13140-13143

⏱ 2021-11-07 11:09:29

📌 抽象使得编写一个大型程序成为可能，将其划分为小而且容易理解的部分，用 C￼这样的高级语言编写这样的程序不用考虑汇编，用汇编写代码不用考虑逻辑门，用逻辑门来构建处理器不用太多考虑晶体管 ^30179184-7-13411-13623

⏱ 2021-11-07 11:10:45

📌 设计和实现操作系统的一个目标，是提供高性能 ^30179184-7-13736-13757

⏱ 2021-11-07 11:11:02

📌 另一个目标是在应用程序之间以及在 OS 和应用程序之间提供保护（protection ^30179184-7-14007-14047

⏱ 2021-11-07 11:13:00

📌 保护是操作系统基本原理之一的核心，这就是隔离（isolation）。让进程彼此隔离是保护的关键，因此决定了 OS 必须执行的大部分任务。 ^30179184-7-14134-14200

⏱ 2021-11-07 11:13:19

📌 这些开销会以多种形式出现：额外时间（更多指令）和额外空间（内存或磁盘上）。如果有可能，我们会寻求解决方案，尽量减少一种或两种。但是，完美并非总是可以实现的，我们会注意到这一点，并且（在适当的情况下）容忍它。 ^30179184-7-13875-13978

⏱ 2021-11-07 11:15:03

📌 虚拟化和让系统易于使用是非常值得的，但不会不计成本。因此，我们必须努力提供虚拟化和其他操作系统功能，同时没有过多的开销。 ^30179184-7-13815-13875

⏱ 2021-11-07 11:15:34

📌 用户坐在计算机前使用它，大部分时间它都会闲置，所以会导致设施每小时浪费数千美元 [BH00 ^30179184-7-15348-15392

⏱ 2021-11-07 11:16:27

📌 在这些老的大型机系统上，一次运行一个程序，由操作员来控制。这个操作员完成了你认为现代操作系统会做的许多事情（例如，决定运行作业的顺序）。如果你是一个聪明的开发人员，就会对这个操作员很好，这样他们可以将你的工作移动到队列的前端。 ^30179184-7-15130-15243

⏱ 2021-11-07 11:16:45

📌 系统调用和过程调用之间的关键区别在于，系统调用将控制转移（跳转）到 OS 中，同时提高硬件特权级别（hardware privilege level）。 ^30179184-7-15928-16002

⏱ 2021-11-07 11:17:14

📌 在内核模式下，操作系统可以完全访问系统的硬件，因此可以执行诸如发起 I/O 请求或为程序提供更多内存等功能。当 ^30179184-7-16210-16263

⏱ 2021-11-07 11:17:40

📌 早期的操作系统，如 DOS（来自微软的磁盘操作系统），并不认为内存保护很重要。因此，恶意程序（或者只是一个编程不好的应用程序）可能会在整个内存中乱写乱七八糟的东西。 ^30179184-7-17795-17876

⏱ 2021-11-07 11:18:07

📌 进程的机器状态的另一部分是寄存器。 ^30179184-9-2248-2265

⏱ 2021-11-08 13:54:05

第 1 部分 虚拟化

📌 第 3 章 关于虚拟化的对话教授：现在我们开始讲操作系统 3 个部分的第 1 部分 —— 虚拟化。学生：尊敬的教授，什么是虚拟化？教授：想象我们有一个桃子。学生：桃子？（不可思议）教授：是的，一个桃子，我们称之为物理（physical）桃子。但有很多想吃这个桃子的人，我们希望向每个想吃的人提供一个属于他的桃子，这样才能皆大欢喜。我们把给每个人的桃子称为虚拟（virtual）桃子。我们通过某种方式，从这个物理桃子创造出许多虚拟桃子。重要的是，在这种假象中，每个人看起来都有一个物理桃子，但实际上不是。学生：所以每个人都不知道他在和别人一起分享一个桃子吗？教授：是的。学生：但不管怎么样，实际情况就是只有一个桃子啊。教授：是的，所以呢？学生：所以，如果我和别人分享同一个桃子，我一定会发现这个问题。教授：是的！你说得没错。但吃的人多才有这样的问题。多数时间他们都在打盹或者做其他事情，所以，你可以在他们打盹的时候把他手中的桃子拿过来分给其他人，这样我们就创造了有许多虚拟桃子的假象，每人一个桃子！ ^30179184-8-375

⏱ 2021-11-07 11:18:54

第 4 章 抽象：进程

📌 进程就是运行中的程序。程序本身是没有生命周期的，它只是存在磁盘上面的一些指令（也可能是一些静态数据）。是 ^30179184-9-433-485

⏱ 2021-11-08 13:50:32

📌 操作系统让这些字节运行起来，让程序发挥作用。 ^30179184-9-485-507

⏱ 2021-11-08 13:50:38

📌 进程的机器状态有一个明显组成部分，就是它的内存 ^30179184-9-2124-2147

⏱ 2021-11-08 13:54:03

📌 类似地，栈指针（stack pointer）和相关的帧指针（frame pointer）用于管理函数参数栈、局部变量和返回地址。 ^30179184-9-2434-2498

⏱ 2021-11-08 13:54:34

📌 机制看成为系统的 “如何（how）” 问题提供答案。例如，操作系统如何执行上下文切换？策略为 “哪个（which）” 问题提供答案。 ^30179184-9-2644-2706

⏱ 2021-11-08 13:56:30

📌 将两者分开可以轻松地改变策略，而不必重新考虑机制 ^30179184-9-2724-2748

⏱ 2021-11-08 13:56:48

📌 所有现代操作系统都以某种形式提供这些 API ^30179184-9-3002-3023

⏱ 2021-11-08 13:57:19

📌 就是程序如何转化为进程。 ^30179184-9-3970-3982

⏱ 2021-11-08 13:57:59

📌 必须为程序的运行时栈（run-time stack 或 stack）分配一些内存。你可能已经知道，C 程序使用栈存放局部变量、函数参数和返回地址。 ^30179184-9-4753-4823

⏱ 2021-11-08 13:59:20

📌 操作系统也可能为程序的堆（heap）分配一些内存。在 C 程序中，堆用于显式请求的动态 ^30179184-9-4920-4961

⏱ 2021-11-08 13:59:27

📌 通过将代码和静态数据加载到内存中，通过创建和初始化栈以及执行与 I/O 设置相关的其他工作，OS 现在（终于）为程序执行搭好了舞台 ^30179184-9-5337-5399

⏱ 2021-11-08 13:59:52

📌 这样做可以通过保持 CPU 繁忙来提高资源利用率。 ^30179184-9-7648-7671

⏱ 2021-11-09 09:27:18

📌 任何时刻，我们都可以清点它在执行过程中访问或影响的系统的不同部分，从而概括一个进程。 ^30179184-9-1943-1985

⏱ 2021-11-09 09:57:14

📌 程序计数器（Program Counter，PC） ^30179184-9-2359-2384

⏱ 2021-11-09 09:58:15

📌 代码和所有静态数据（例如初始化变量）加载（load）到内存中，加载到进程的地址空间中。 ^30179184-9-4061-4104

⏱ 2021-11-09 09:59:03

📌 启动程序，在入口处运行，即 main ()。通过跳转到 main () 例程（第 5 章讨论的专门机制），OS 将 CPU 的控制权转移到新创建的进程中，从而程序开始执行。 ^30179184-9-5411-5487

⏱ 2021-11-09 09:59:53

📌 有一些关键的数据结构来跟踪各种相关的信息。例如，为了跟踪每个进程的状态，操作系统可能会为所有就绪的进程保留某种进程列表（process list） ^30179184-9-7865-7938

⏱ 2021-11-09 10:01:11

📌 以及跟踪当前正在运行的进程的一些附加信息。操作系统还必须以某种方式跟踪被阻塞的进程。 ^30179184-9-7939-7981

⏱ 2021-11-09 10:01:18

📌 当一个进程停止时，它的寄存器将被保存到这个内存位置。通过恢复这些寄存器（将它们的值放回实际的物理寄存器中），操作系统可以恢复运行该进程。 ^30179184-9-8217-8285

⏱ 2021-11-09 10:01:40

📌 这是比较简单的一种，但是，任何能够同时运行多个程序的操作系统当然都会有类似这种结构的东西，以便跟踪系统中正在运行的所有程序。有时候人们会将存储关于进程的信息的个体结构称为进程控制块（Process Control Block，PCB），这是谈论包含每个进程信息的 C 结构的一种方式。 ^30179184-9-10235-10375

⏱ 2021-11-09 10:02:23

📌 模拟作业以模拟器的形式出现 ^30179184-9-12159-12172

⏱ 2021-11-09 10:05:10

第 5 章 插叙：进程 API

📌 这种分离 fork () 及 exec () 的做法在构建 UNIX shell 的时候非常有用，因为这给了 shell 在 fork 之后 exec 之前运行代码的机会，这些代码可以在运行新程序前改变环境，从而让一系列有趣的功能很容易实现。 ^30179184-10-7408-7515

⏱ 2021-11-09 10:06:46

📌 让我们更详细地理解一下 p1.c 到底发生了什么 ^30179184-10-2186-2208

⏱ 2021-11-09 10:08:55

📌 UNIX 系统采用了一种非常有趣的创建新进程的方式，即通过一对系统调用：fork () 和 exec () ^30179184-10-631-679

⏱ 2021-11-09 10:09:06

第 6 章 机制：受限直接执行

📌 运行一个进程一段时间，然后运行另一个进程，如此轮换。通过以这种方式时分共享（time sharing）CPU，就实现了虚拟化。 ^30179184-11-456-519

⏱ 2021-11-09 10:09:44

📌 第一个是性能：如何在不增加系统开销的情况下实现虚拟化？第二个是控制权：如何有效地运行进程，同时保留对 CPU 的控制？控制权对于操作系统尤为重要，因为操作系统负责资源管理。如果没有控制权，一个进程可以简单地无限制运行并接管机器，或访问没有权限的信息。因此，在保持控制权的同时获得高性能，这是构建操作系统的主要挑战之一。 ^30179184-11-570-727

⏱ 2021-11-09 10:09:55

第 15 章 机制：地址转换

📌 1．用种子 1、2 和 3 运行，并计算进程生成的每个虚拟地址是处于界限内还是界限外？如果在界限内，请计算地址转换。2．使用以下标志运行：-s 0 -n 10。为了确保所有生成的虚拟地址都处于边界内，要将 - l（界限寄存器）设置为什么值？3．使用以下标志运行：-s 1 -n 10 -l 100。可以设置界限的最大值是多少，以便地址空间仍然完全放在物理内存中？4．运行和第 3 题相同的操作，但使用较大的地址空间（-a）和物理内存（-p）。5．作为边界寄存器的值的函数，随机生成的虚拟地址的哪一部分是有效的？画一个图，使用不同随机种子运行，限制值从 0 到最大地址空间大小。 ^30179184-20-14592

⏱ 2021-10-13 09:08:27

第 16 章 分段

📌 如果我们将整个地址空间放入物理内存，那么栈和堆之间的空间并没有被进程使用，却依然占用了实际的物理内存。 ^30179184-21-548-599

⏱ 2021-10-13 09:14:00

📌 怎样支持大地址空间 ^30179184-21-761-770

⏱ 2021-10-13 09:14:04

📌 分段：泛化的基址 / 界限 ^30179184-21-975-986

⏱ 2021-10-13 09:14:08

📌 一个基址和界限寄存器对，而是给地址空间内的每个逻辑段（segment）一对。一个段只是地址空间里的一个连续定长的区域，在典型的地址空间里有 3 个逻辑不同的段：代码、栈和堆。 ^30179184-21-1106-1191

⏱ 2021-10-14 12:41:07

第 26 章 并发：介绍

📌 第 26 章　并发：介绍 ^30179184-33-350-360

⏱ 2022-01-14 09:15:03

第 46 章 关于分布式的对话

📌 消息会丢失，机器会故障，磁盘会损坏数据，就像整个世界都在和你作对！ ^30179184-51-1170-1203

⏱ 2021-11-09 10:10:42

第 47 章 分布式系统

📌 不只是与一台机器进行交互 ^30179184-52-510-522

⏱ 2021-11-09 10:11:50

📌 我们关注的主要是故障（failure） ^30179184-52-670-689

⏱ 2021-11-09 10:12:02

📌 系统性能（performance）通常很关键 ^30179184-52-1471-1493

⏱ 2021-11-09 10:12:41

📌 故障将是重点：通信层应如何处理故障？ ^30179184-52-1824-1842

⏱ 2021-11-09 10:13:35

📌 丢包是网络的基本现象。所以问题变成：应该如何处理丢包？ ^30179184-52-2489-2516

⏱ 2021-11-09 10:14:16

📌 现代网络的核心原则是，通信基本是不可靠的 ^30179184-52-1933-1953

⏱ 2021-11-09 10:14:30

📌 数据包都会经常丢失、损坏，或无法到达目的地。 ^30179184-52-1990-2012

⏱ 2021-11-09 10:14:34

📌 这种不可靠层的一个很好的例子，就是几乎所有现代系统中都有的 UDP/IP 网络栈 ^30179184-52-2727-2765

⏱ 2021-11-09 10:15:39

📌 UDP 是不可靠通信层的一个很好的例子 ^30179184-52-5420-5438

⏱ 2021-11-09 10:19:55

📌 使用校验和检查完整性 ^30179184-52-5705-5715

⏱ 2021-11-09 10:20:08

📌 我们需要一种额外的机制，称为超时（timeout ^30179184-52-6866-6890

⏱ 2021-11-09 10:20:40

📌 超时和重试的组合导致一些人称这种方法为超时 / 重试（timeout/retry）。非常聪明的一群人，那些搞网络的，不是吗？图 47.4 展示了一个例子。 ^30179184-52-7020-7122

⏱ 2021-11-09 10:20:53

📌 这个例子中，丢失的不是原始消息，而是确认消息 ^30179184-52-7165-7187

⏱ 2021-11-09 10:21:01

📌 因此，发送方收到确认，但消息未被接收两次，保证了上面提到的一次性语义。 ^30179184-52-7992-8027

⏱ 2021-11-09 10:21:41

📌 该机制被称为顺序计数器 ^30179184-52-8195-8206

⏱ 2021-11-09 10:21:59

📌 发送消息后，发送方递增该值（到 N + 1） ^30179184-52-8334-8383

⏱ 2021-11-09 10:22:42

📌 因此接收器知道它已经接收到该消息。因此它会确认该消息，但不会将其传递给应用程序。以这种简单的方式，顺序计数器可以避免重复。 ^30179184-52-8754-8815

⏱ 2021-11-09 10:23:06

📌 常用的可靠通信层称为 TCP/IP，或简称为 TCP ^30179184-52-8845-8869

⏱ 2021-11-09 10:23:09

📌 分布式共享内存（Distributed Shared Memory，DSM）系统使不同机器上的进程能够共享一个大的虚拟地址空间 [LH89] ^30179184-52-9727-9796

⏱ 2021-11-09 10:23:46

📌 例如，在第一次超时之后，客户端可能会将其超时值增加到更高的量，可能是原始值的两倍。这种指数倒退（exponential back-off）方案，在早期的 Aloha 网络中实施，并在早期的以太网 [A70] 中采用，避免了资源因过量重发而过载的情况。健壮的系统力求避免这种过载。 ^30179184-52-9504-9639

⏱ 2021-11-09 10:24:08

📌 另一个问题是性能 ^30179184-52-10271-10279

⏱ 2021-11-09 10:25:11

📌 远程过程调用（RPC ^30179184-52-10496-10506

⏱ 2021-11-09 10:25:57

📌 主要的抽象是基于远程过程调用（Remote Procedure Call），或简称 RPC [BN84]￼。 ^30179184-52-10591-10757

⏱ 2021-11-09 10:26:11

📌 其余的由 RPC 系统处理，RPC 系统通常有两部分：存根生成器（stub generator，有时称为协议编译器，protocol compiler）和运行时库（run-time library）。接下来将更详细地介绍这些部分。 ^30179184-52-10882-10994

⏱ 2021-11-09 10:26:30

📌 并发性的服务器组织方式 ^30179184-52-13277-13288

⏱ 2021-11-09 10:29:00

📌 如果一个 RPC 调用阻塞（例如，在 I/O 上），就会浪费服务器资源。因此，大多数服务器以某种并发方式构造。常见的组织方式是线程池（thread pool） ^30179184-52-13342-13417

⏱ 2021-11-09 10:29:16

📌 线程不断接收其他请求，并可能将其发送给其他工作线程。 ^30179184-52-13488-13514

⏱ 2021-11-09 10:29:27

📌 当前互联网协议提供的主机名和端口号 ^30179184-52-13872-13889

⏱ 2021-11-09 10:37:17

📌 可靠的通信层之上构建 RPC 可能会导致性能的低效率 ^30179184-52-14371-14395

⏱ 2021-11-09 10:37:43

📌 可靠的通信层如何工作：确认和超时 / 重试 ^30179184-52-14404-14423

⏱ 2021-11-09 10:37:48

📌 许多 RPC 软件包都建立在不可靠的通信层之上，例如 UDP。 ^30179184-52-14580-14608

⏱ 2021-11-09 10:37:59

📌 47.6 小结我们介绍了一个新主题，分布式系统及其主要问题：如何处理故障现在是常见事件。正如人们在 Google 内部所说的那样，当你只有自己的台式机时，故障很少见。当你拥有数千台机器的数据中心时，故障一直在发生。所有分布式系统的关键是如何处理故障。我们还看到，通信是所有分布式系统的核心。在远程过程调用（RPC）中可以看到这种通信的常见抽象，它使客户端能够在服务器上进行远程调用。RPC 包处理所有细节，包括超时 / 重试和确认，以便提供一种服务，很像本地过程调用。真正理解 RPC 包的最好方法，当然是亲自使用它。Sun 的 RPC 系统使用存根编译器 rpcgen，它是很常见的，在当今的许多系统上可用，包括 Linux。尝试一下，看看所有这些麻烦到底是怎么回事。参考资料 [A70]“The ALOHA System — Another Alternative for Computer Communications”Norman AbramsonThe 1970 Fall Joint Computer ConferenceALOHA 网络开创了网络中的一些基本概念，包括指数倒退和重传。多年来，这些已成为共享总线以太网网络通信的基础。[BN84]“Implementing Remote Procedure Calls” Andrew D. Birrell, Bruce Jay NelsonACM TOCS, Volume 2:1, February 1984 基础 RPC 系统，其他所有理论都基于此。是的，它是我们在 Xerox PARC 的朋友们的另一项开创性努力的结果。 ^30179184-52-16865

⏱ 2021-11-09 10:38:39

📌 要构建可靠的文件传输，必须包括端到端的可靠性检查，例如，在整个传输完成后，读取接收方磁盘上的文件，计算校验和，并将该校验和与发送方文件的校验和进行比较。按照这个准则的推论是，有时候，较低层提供额外的功能确实可以提高系统性能，或在其他方面优化系统。因此，不应该排除在系统中较低层的这种机制。实际上，你应该小心考虑这种机制的实用性，考虑它最终对整个系统或应用程序的作用。RPC 包通常在其消息格式中提供明确定义的字节序，从而处理该问题。在 Sun 的 RPC 包中，XDR（eXternal Data Representation，外部数据表示）层提供此功能。如果发送或接收消息的计算机与 XDR 的字节顺序匹配，就会按预期发送和接收消息。 ^30179184-54-14643

⏱ 2022-01-14 09:14:39

第 48 章 Sun 的网络文件系统（NFS）

📌 分布式客户端 / 服务器计算的首次使用之一，是在分布式文件系统领域。在这种环境中，有许多客户端机器和一个服务器（或几个）。服务器将数据存储在其磁盘上，客户端通过结构良好的协议消息请求数据。图 48.1 展示了基本设置。[插图] 图 48.1 一般的客户端 / 服务器系统从图中可以看到，服务器有磁盘，发送消息的客户端通过网络，访问服务器磁盘上的目录和文件。为什么要麻烦，采用这种安排？（也就是说，为什么不就让客户端用它们的本地磁盘？）好吧，这种设置允许在客户端之间轻松地共享（sharing）数据。因此，如果你在一台计算机上访问文件（客户端 0），然后再使用另一台（客户端 2），则你将拥有相同的文件系统视图。你可以在这些不同的机器上自然共享你的数据。第二个好处是集中管理（centralized administration）。例如，备份文件可以通过少数服务器机器完成，而不必通过众多客户端。另一个优点可能是安全（security），将所有服务器放在加锁的机房中。关键问题：如何构建分布式文件系统如何构建分布式文件系统？要考虑哪些关键方面？哪里容易出错？我们可以从现有系统中学到什么？ ^30179184-53-359

⏱ 2021-11-09 10:40:01

读书笔记

第 4 章 抽象：进程

划线评论

📌 目前还不清楚这是不是一个很好的决定 ^7265636-7uG78TXby

💭 可以按优先级分配，如果立即切换可能造成一个频繁切换上下文，效率更低．

⏱ 2021-11-09 09:28:51

划线评论

📌 ，当进程向磁盘发起 I/O 请求时，它会被阻塞，因此其他进程可以使用处理器。 ^7265636-7uG6WqgFc

💭 线程阻塞

⏱ 2021-11-09 09:25:47

第 16 章 分段

划线评论

📌 分段（segmentation）的概念应运而生。分段并不是一个新概念，它甚至可以追溯到 20 世纪 60 年代初期 [H61, G62]。这个想法很简单，在 MMU 中引入不止一个基址和界限寄存器对，而是给地址空间内的每个逻辑段（segment）一对。一个段只是地址空间里的一个连续定长的区域，在典型的地址空间里有 3 个逻辑不同的段：代码、栈和堆。分段的机制使得操作系统能够将不同的段放到不同的物理内存区域，从而避免了虚拟地址空间中的未使用部分占用物理内存。 ^7265636-7u2LJkG3F

💭 原来如此呀

⏱ 2021-10-14 12:41:45

第 46 章 关于分布式的对话

划线评论

📌 复制，重试，以及各种其他技巧。人们随着时间的推移开发了这些技巧，用于检测故障，并从故障中恢复。 ^7265636-7uG9Uidei

💭 分布式一

⏱ 2021-11-09 10:11:04

第 47 章 分布式系统

划线评论

📌 在第一种（最佳）情况下，页面已经是机器上的本地页面，因此可以快速获取数据。在第二种情况下，页面目前在其他机器上。发生页面错误，页面错误处理程序将消息发送到其他计算机以获取页面，将其装入请求进程的页表中，然后继续执行。 ^7265636-7uGaS0mwP

💭 不实用．稳定性和性能问题

⏱ 2021-11-09 10:25:46

划线评论

📌 为了让接收方能够检测重复的消息传输，发送方必须以某种独特的方式标识每个消息，并且接收方需要某种方式来追踪它是否已经看过每个消息 ^7265636-7uGaER0eS

💭 原来序号是这样来的、确认可能丢失

⏱ 2021-11-09 10:22:32

划线评论

📌 为了让接收方能够检测重复的消息传输，发送方必须以某种独特的方式标识每个消息，并且接收方需要某种方式来追踪它是否已经看过每个消息 ^7265636-7uGaC1Akt

💭 序号

⏱ 2021-11-09 10:21:50

本书评论