Lesson 06 模块化
这一讲的核心在于模块化。我们将看到系统设计如何从脆弱的“软模块化”演进到具有物理隔离能力的“强制模块化”,并探讨 RPC 和分布式文件系统(NFS)是如何在现实中应用这些原则的。
1. 软模块化的局限:命运共享
在传统的单体程序中,人们虽然使用了函数和对象进行模块化,但这被称为软模块化。
- 风险:这种隔离只在源代码层面有效。在运行时,所有代码共享同一个栈、寄存器和内存空间。
- 命运共享(Fate Sharing):一个模块的指针越界或栈溢出,会直接破坏其他模块的数据,甚至导致整个系统崩溃。调用者和被调用者在错误面前是同生共死的。
- 结论:软模块化无法防止错误的传播,也无法提供真正的安全隔离。
2. 强制模块化:C/S 架构与 RPC
为了实现真正的隔离,我们需要强制模块化。其核心思想是将模块部署在不同的物理设备或受保护的地址空间中,这就是典型的客户端/服务器(C/S)架构。
这是一个非常重要的架构
- 消息传递:模块间不再通过共享内存交互,而是通过请求-响应的消息进行通信。
- 收益:
- 故障隔离:服务器崩溃不会导致客户端挂掉,反之亦然。
- 安全隔离:一个模块无法直接访问另一个模块的内部状态。
- RPC(远程过程调用):
- 目标:让远程调用看起来像本地调用一样简单。
- 挑战:虽然接口看起来一样,但语义完全不同。网络存在时延、丢包和超时。
- 处理机制:我们需要决定是采用
at-least-once(至少一次,适合幂等操作)还是at-most-once(至多一次)来处理网络的不确定性。
3. 案例分析:NFS 及其“无状态”设计
NFS(网络文件系统)是体现强制模块化权衡的经典案例。它的目标是让远程文件访问像本地文件一样透明。
- 设计冲突:本地文件系统通常是有状态的(保存文件描述符、偏移量),但这种设计在网络环境下很脆弱——如果服务器重启,客户端持有的状态就会失效。
- 无状态性:服务端不保存客户端的任何状态
- 每次 RPC 请求都携带完整的参数(如文件句柄和绝对偏移量)。
- 收益:极强的容错性。如果服务器挂了,重启后客户端可以直接继续发送请求,无需复杂的恢复过程。
- vnode 抽象层:
- 为了屏蔽本地文件系统和远程文件系统的差异,UNIX 引入了
vnode层。无论数据在本地磁盘还是在千里之外的服务器,用户看到的都是统一的接口。
- 为了屏蔽本地文件系统和远程文件系统的差异,UNIX 引入了
4. 对照
Lesson 06 Modularity
The core of this lesson is Modularity. We will see how system design evolves from fragile “Soft Modularity” to “Hard Modularity” with physical isolation, and explore how RPC and Distributed File Systems (NFS) apply these principles in practice.
1. The Limitations of Soft Modularity: Fate Sharing
In traditional monolithic programs, although we use functions and objects for modularity, this is referred to as Soft Modularity.
- Risks: This isolation is only effective at the source code level. At runtime, all code shares the same stack, registers, and memory space.
- Fate Sharing: A pointer out-of-bounds error or a stack overflow in one module can directly corrupt the data of other modules or even crash the entire system. The caller and callee live and die together in the face of errors.
- Conclusion: Soft modularity cannot prevent the propagation of errors, nor can it provide true security isolation.
2. Hard Modularity: C/S Architecture and RPC
To achieve true isolation, we need Hard Modularity. The core idea is to deploy modules on different physical devices or within protected address spaces, which is exemplified by the Client/Server (C/S) architecture.
This is a critically important architecture.
- Message Passing: Modules no longer interact through shared memory; instead, they communicate via Request-Response messages.
- Benefits:
- Fault Isolation: A server crash does not cause the client to hang, and vice versa.
- Security Isolation: One module cannot directly access the internal state of another.
- RPC (Remote Procedure Call):
- Goal: To make a remote call look as simple as a local one.
- Challenge: While the interface looks the same, the semantics are entirely different. Networks introduce latency, packet loss, and timeouts.
- Handling Mechanisms: We must decide whether to use
at-least-once(suitable for idempotent operations) orat-most-onceto handle network uncertainty.
3. Case Study: NFS and its “Stateless” Design
NFS (Network File System) is a classic case study in the trade-offs of hard modularity. Its goal is to make remote file access as transparent as local file access.
- Design Conflict: Local file systems are typically stateful (storing file descriptors and offsets), but this design is fragile in a network environment—if the server reboots, the state held by the client becomes invalid.
- Statelessness: The server does not store any state regarding the client.
- Each RPC request carries complete parameters (such as file handles and absolute offsets).
- Benefits: Exceptional fault tolerance. If the server crashes, the client can simply continue sending requests after the reboot without a complex recovery process.
- vnode Abstraction Layer:
- To hide the differences between local and remote file systems, UNIX introduced the
vnodelayer. Whether the data is on a local disk or a server thousands of miles away, the user sees a unified interface.
- To hide the differences between local and remote file systems, UNIX introduced the