LiS:Linux STREAMS

作者:Graham Wheeler

UNIX 中的输入/输出系统远非简单,它涉及许多不同的模块:网络涉及排列在协议栈中的不同协议阶段;终端 I/O 涉及堆叠在(可能是网络)设备之上的不同“线路规程”。所有这些模块都对现有的 I/O 数据流执行一些处理。

在 Linux 和大多数 BSD 系统中,I/O 模块位于内核内部,它们之间的关系或多或少地硬编码到代码中。例如,TCP/IP 协议栈是一组精心编程的模块,它们之间具有很强的相互关系。它旨在在典型配置下良好工作。

STREAMS 是一种灵活的输入/输出系统,最初旨在克服以前 UNIX 系统中发现的缺乏灵活性(参见参考资料 1)。它是套接字的替代方案,并在大多数商业 UNIX 版本中使用。如果我们希望来自 Solaris、Unixware 等系统的网络软件能够在 Linux 上开箱即用,则需要某种 STREAMS。

STREAM 定义

STREAM(参见图 1)本质上是一个动态可配置的模块堆栈。每个模块在数据流从设备到用户或反之亦然的过程中对数据流进行一些处理。用户将 STREAM 视为文件。它使用常用的 openreadwriteioctlclose 系统调用进行处理。用户写入的数据被打包成消息,这些消息被向下游发送。用户读取的数据来自底层设备驱动程序向上游发送的消息。

LiS: Linux STREAMS

图 1. STREAM 的组件

几个额外的系统调用 putpmsggetpmsg 允许用户直接发送和接收 STREAMS 消息。另一个系统调用 pollselect 提供了替代接口。因此,每个 STREAM 都由以下元素组成

  • 强制性的 STREAM 头 与执行 I/O 的用户进程通信。头填充了用户系统调用和消息流之间的空白。因此,写入 STREAM 由头处理,方法是向下游发送消息。相反,向上游的数据消息被头用于服务于 STREAM 上的读取系统调用。

  • (可能为空的)STREAM 模块堆栈通常对通过的消息执行一些计算,并将它们向上游或下游转发。例如,X.25 封装上的 IP (IXE) 可以实现为 STREAMS 模块;IP 数据包在通过 IXE 模块时将被(解)封装。终端线路规程模块是另一个示例;键入的字符可以在穿过线路规程模块时被 cooked。因此,数据包嗅探器模块可以用作诊断或调试工具。

  • 强制性的 STREAM 驱动程序将 STREAM 连接到位于其下方的设备。STREAM 驱动程序也可以仅是软件;例如,STREAM 驱动程序可以用于为内核实现 SNMP MIB,或者可以编写驱动程序来模拟真实硬件驱动程序的行为以用于开发目的。

STREAMS 的一个优点是不同的模块(或驱动程序)可以很容易地解耦。因此,它们可以由不知道它们将在哪里使用的实际协议栈的不同人员独立开发,前提是各种模块和驱动程序之间的接口定义良好。STREAMS 包括传输层、网络层和数据链路层的标准接口。此外,模块可以动态地“推送”到 STREAM 上(和从 STREAM 上弹出),这是一个非常方便的功能。

最后,特殊的 多路复用器 驱动程序允许多个 STREAM 多路复用到另一个(或多个)STREAM 中。图 2 中的 ip 模块是一个多路复用器。在此示例中,它使用以太网驱动程序或 IP-on-X.25 驱动程序多路复用 TCP 和 IP 消息。可以构建完整的 STREAMS 网络(参见图 2),并且可以动态设置许多不同的协议栈以进行操作。

LiS: Linux STREAMS

图 2. STREAMS 网络

LiS 项目

在 Linux 可用之前,Dennis M. Ritchie 为 UNIX 第九版设计了 STREAMS(参见参考资料 5)。从那时起,STREAMS 概念得到了改进和修订,适用于不同的操作系统。从 UNIX SVR4 STREAMS(参见参考资料 1)到 Plan 9 Streams(参见参考资料 3)的变体今天仍然存在。

不幸的是,SVR4 使第九版 STREAMS 的简洁和清晰的设计复杂化,主要是为了添加新功能,例如原子收集/分散写入和多处理器支持。管道和 fifo 等不同的设备也使用 STREAMS 重新实现(参见图 3)。

LiS: Linux STREAMS

图 3. 基于 STREAMS 的管道

尽管比 Ritchie 的 Streams 复杂得多,但 SVR4 STREAMS 简化了网络软件的构建。实际上,UNIX System V 机器的大多数网络软件都是使用 STREAMS 编写的(包括套接字接口)。我们希望能够在 Linux 下运行 SVR4 驱动程序软件。

在我们中的一些人独立开始开发后来成为 LiS 的东西之后,我们在 c.o.l.a. 上会面,并决定协调我们的工作。LiS 项目 LSM 于 1995 年 3 月发布,项目开始。

使用 STREAM 还是不使用 STREAM

使用 STREAMS 有几个原因:不同层的标准服务接口(数据链路层的 DLPI、网络层的 NPI 和传输层的 TPI)、灵活性和 SVR4 兼容性。

我们喜欢 SVR4 下的 STREAMS 的原因是我们可以编写符合强大但不繁琐的 API(特别是 DLPI 或 TPI)的设备驱动程序,并让现有的网络服务 (DLPI) 或现有的“应用程序”(读取 非内核代码)与该设备一起工作,而无需额外的努力。

协议阶段(或模块)可以动态添加/删除。想象一下,您正在调试 X.25 的传输层接口 (TLI),您可以多次推送和弹出 x25_tli 模块。也就是说,它是一个开放框架。所使用的这些模块当然可以共享并在不同的地方重用。使用套接字,您只能拥有内核拥有的东西。

最重要的是,标准是“好东西”。在分布式系统时代,这同样适用于内核级网络和通信接口。STREAMS 框架、API 和服务接口是由 AT&T Bell Labs 的聪明人设计的。结果是一种机制,它既清晰、全面又优雅。

有人反对 STREAMS,认为与 BSD 套接字相比,它们太复杂且太慢。一个相关的论点是,使用 BSD 风格的协议栈可以更有效地完成 TCP/IP 网络。

请考虑这一点:Linux TCP/IP 网络代码可以与 LiS 一起按原样使用。LiS 的目的只是提供可用的 STREAMS 框架,而不是替换 Linux TCP/IP 协议栈。现有网络软件被 STREAMS 用户视为图 2 中的虚线框。我们需要的是与现有 Linux 驱动程序和协议栈接口的伪模块。

关于简单性,与套接字相比,STREAMS 使某些事情(例如“深层”协议栈)更简单。套接字旨在实现 TCP/IP 类型网络,虽然简单,但不是 可扩展的。也就是说,您不能轻易使用套接字机制来构建套接字没有内置知识的深层协议栈。每次 Internet 协议套件需要另一层时,都可能对套接字进行一些 hack。

Internet 协议套件比内核实现者喜欢认为的更深。考虑 TCP/IP,当 IP 使用 X.25 数据包发送时,通过 LAPB 帧通过驱动程序传输。现在您有一个 TCP/IP <-> X.25 <-> LAPB <-> 驱动程序栈。然后添加另一个 TCP/IP 上的协议(例如,NFS)并插入帧中继 (FR)。堆栈变为:NFS <-> TCP/IP <-> X.25 <-> LAPB <-> FR <-> 驱动程序。套接字没有配备构建最初未设计到其中的协议栈。它可以完成,但使用 STREAMS 更容易和更简洁。

SVR4 STREAMS 和 LiS 比第九版 STREAMS 复杂得多,但增加的复杂性主要在于 STREAMS 实现,并且对驱动程序或模块程序员是隐藏的。

让 STREAM 自由流动

大多数 UNIX 功能都以源代码形式提供,供人们阅读和使用。STREAMS 是一个明显的例外。因此,即使我们可以将 LiS 设计为仅支持 STREAMS 接口,我们也试图遵循其设计。如果 SVR4 STREAMS 代码可用,则该项目可以简化为简单的移植。因此,设计指南是几本书的混合,展示了 STREAMS 的工作原理(参见参考资料 2 和 4)。

Linux 内核源代码的可用性至关重要,因为 LiS 需要对现有内核子系统进行少量更改。

从头开始,我们的目标是使 LiS 可移植,以便其他人可以避免重写它以在不同的系统上使用。通过替换单个小型模块,可以将整个框架移植到不同的操作系统。LiS 可移植性通过 Gcom 已将其移植到 QNX(一种 UNIX 版本)这一事实得到证明。将来,无需太多努力即可完成 BSD UNIX 系统甚至 NT 的移植。

LiS 功能和实现

完整的 STREAMS 描述对于本文来说太长了。您可能会阅读一些书籍以了解有关 STREAMS 的更多信息,这些书籍可以在参考资料中找到。简而言之,LiS 功能包括

  • 支持典型的 STREAMS 模块和驱动程序

  • 能够使用仅二进制驱动程序

  • 方便的调试工具

典型的 STREAMS 设施

LiS 和 SVR4 STREAMS 的实现之间存在许多相似之处。这是因为最初的项目成员遵循“魔术花园”(参见参考资料 2)作为设计指南。当前的维护人员也深受 SVR4 STREAMS 的影响,因为他们自 1990 年以来一直在为 SVR4 编写 STREAMS 驱动程序。因此,流头结构、队列结构、消息结构等都遵循 SVR4 模型。

两者之间确实存在差异。SVR4 不允许 STREAMS 多路复用器在堆栈的多个级别使用相同的驱动程序。例如,如果我们有一个名为“DLPI”的 STREAMS 多路复用器驱动程序和另一个名为“NPI”的驱动程序,则 SVR4 STREAMS 将不允许堆栈:NPI(SNA) <-> DLPI(QLLC) <-> NPI(X.25) <-> DLPI(LAPB)。LiS 允许这些组合,因为我们认为这种配置没有危害。

用于 LiS 的配置文件仿照 SVR4 sdevicemdevice 文件建模。但是,LiS 语法不同,并将 SVR4 用于指定两个文件的功能组合到一个文件中。LiS 构建过程 (Makefiles) 允许单个驱动程序拥有自己的配置文件。它们都组合成一个主配置文件,然后用于在构建时配置 LiS。

在 SVR4 中,STREAMS 执行程序是内核的可链接包。它不是硬编码到内核中的。使用 LiS,STREAMS 执行程序实际上是内核的运行时、可加载模块,比 SVR4 STREAMS 更动态一步。

LiS 实现的快速概述将揭示 STREAM 是模块的全双工链(参见图 4)。每个模块都由一个队列对组成:一个用于正在读取的数据,另一个用于正在写入的数据。每个模块都有几个数据结构,提供所需的操作(即函数),以及统计信息和其他数据。

LiS: Linux STREAMS

图 4. STREAM 中的队列

模块操作由程序员提供,包括用于处理上游和下游消息的过程。消息可以排队以进行延迟处理,因为 LiS 保证在可以处理排队的消息时调用服务过程。

LiS 的大部分实现都处理这些队列以及用于通过 STREAM 发送消息的消息数据结构。消息携带类型代码,并由一个或多个消息块组成。只有指向消息的指针从一个模块传递到下一个模块,因此没有数据复制开销。

STREAM 的头部是另一个有趣的软件。在图 5 中,您可以看到如何从 Linux VFS(虚拟文件系统)层访问它,该层将内核与文件系统连接起来。请注意,即使 Linux 没有干净且隔离的 VFS 层,Linux i-节点在精神上也是 v-节点,并且其文件系统层可以被认为是 VFS。有关实现的实际描述,请阅读“魔术花园”(参考资料 2)的第 7 章。

LiS: Linux STREAMS

图 5. STREAM 头

仅二进制驱动程序

LiS 还为与仅二进制驱动程序链接做好了准备。这允许 Gcom 等拥有专有驱动程序的公司将其驱动程序代码移植到 LiS 并分发二进制文件。如果我们期望公司将其现有的 SVR4 STREAMS 驱动程序移植到 LiS,这是一个重要的功能。可用的驱动程序越多,Linux 内核功能就越增强。

调试工具

LiS 调试功能特别方便,并显示了与 SVR4 的另一个不同之处。

当然,这些工具包括一些通用调试实用程序,例如消息打印机,但也包括可以真正帮助调试的重要辅助工具,例如选择性跟踪的能力;例如,getmsg 调用。

内存分配器将文件和行号保存在靠近分配的内存区域的位置。将此与打印出所有正在使用的内存区域的功能结合起来,您就拥有了一个用于查找驱动程序中内存泄漏的工具。

使用统计信息旨在提供帮助,而不是用不必要的信息使使用者负担过重。streams 命令打印出有关 LiS 操作的许多有用信息。甚至还有一个调试位图来使 LiS 触发不同的调试工具。其中之一是使用高分辨率计时器对各种操作进行计时的能力。因此,用户可以使用 LiS 工具获得驱动程序的精细计时,而无需在驱动程序中添加额外的代码。

最后但并非最不重要的一点是,LiS 允许通过模拟整个 STREAMS 框架在用户空间中进行模块调试。模块可以在用户空间中轻松开发,然后在工作时下载到内核中。这是通过 LiS 的“端口”实现的,该端口在 Linux 上的用户空间中(以虚拟方式)运行。

STREAMS 模块可以通过用测试模块包围它们,然后通过被测模块驱动已知的消息序列来测试。LiS 环回驱动程序适用于放置在被测驱动程序下方,因为它表现得像一个简单的回显服务器。流头很可能就是上面需要的全部。

STREAMS 与 Linux TCP/IP 协同工作

整个 TCP/IP 堆栈可以重用;因此,STREAMS 的 TCP/IP 性能不是问题。LiS 附带一个适配器驱动程序,该驱动程序位于标准 Linux IP 下方,并使用 DLPI 连接到 STREAMS 驱动程序。Gcom 使用它来连接其基于 STREAMS 的帧中继和(即将推出的)X.25。

此外,一个贡献的驱动程序(在撰写本文时位于 Dave 的收件箱中)将与 LiS 一起分发,它位于任何 Linux MAC(强制访问控制)驱动程序之上作为客户端,并在上方呈现 DLPI 接口。Gcom 可能会使用此驱动程序将其 SNA(系统网络体系结构)连接到 Linux 令牌环驱动程序。

LiS 许可

LiS 使用 GNU 库公共许可证获得许可,以便公司可以将其现有的 SVR4 专有 STREAMS 驱动程序移植到 LiS 并在 Linux 中使用它们,而无需发布其源代码。如果我们要鼓励公司使用其“镇店之宝”产品支持 Linux,这一点非常重要。

LiS 的最终需求

如果运行 LiS 所需的支持可以包含在主流内核中,这将有所帮助。我们主要指的是新的系统调用和其他小的 钩子,而不是 LiS 本身。这种支持将使人们更容易下载 LiS 并安装它,而无需修补内核。

参考资料

Graham Wheeler (gram@cdsec.com) 于 1996 年在开普敦大学获得计算机网络性能分析博士学位。随后,他花费了大量时间开发 STREAMS 设备驱动程序和协议转换模块,以使许多金融机构能够连接到 PayNet,这是一个大型电子商务支付清算中心。他是 Citadel Data Security 的创始人兼技术总监,该公司专门从事 Internet 防火墙和虚拟专用网络软件开发。

Francisco J. Ballesteros (nemo@gsyc.inf.uc3m.es) 于 1998 年在西班牙马德里理工大学获得计算机科学博士学位。他目前在马德里卡洛斯三世大学教授和研究分布式和可适应的操作系统,并与伊利诺伊大学厄巴纳-香槟分校的系统软件研究小组密切合作。

Denis Froschauer 是 LiS 开发早期实施阶段的重要贡献者。

David Grothe (dave@gcom.com) 是 Gcom, Inc. 的总裁。Gcom 为 UNIX 系统(包括 Linux)生产数据通信协议栈。Grothe 先生于 1979 年在公司成立 Gcom 之前,曾在现称为 Advanced Computer Communications (ACC) 的公司工作,在那里他在 1977 年编写了他的第一个 X.25 实现。在此之前,他曾在伊利诺伊大学厄巴纳-香槟分校担任专业程序员。

加载 Disqus 评论