Linux 打印系统概述

作者:Stephane Peter

本文简要概述了大多数 Linux 系统上使用的主要打印系统,并介绍了 UNIX 打印核心的概念和程序。最后,我们将探讨 Linux 打印的未来以及它如何快速改进。

重要的是要理解,Unix 世界中的打印几乎完全围绕 PostScript 页面描述语言展开,PostScript 页面描述语言由 Adobe 公司开发,是一种成熟的编程语言,用于描述文档每页的内容。如今,许多打印机都嵌入了 PostScript 解释器,它负责使用 PostScript 描述将页面渲染到纸上。所有具有打印选项的现代 Linux 桌面应用程序都将生成 PostScript 数据以打印整页文档。

这种方法与其他面向桌面的操作系统截然不同,并且由此产生了使 Unix 打印成为一项艰巨任务的大部分问题。像 Windows 或 MacOS 这样的操作系统具有更紧密集成的 API,可供应用程序使用,通常会公开打印机的功能并提供抽象层,以便应用程序不必担心设备特定的细节。此外,打印 API 通常与用于在屏幕上显示的图形 API 集成,而这在 X11 中尚未实现。

在大多数 Unix 系统上,唯一可用的接口几乎可以归结为“将作业提交到队列并希望它能正确打印”。没有统一的方法来收集打印机或作业状态,这严重削弱了 Linux 应用程序在打印方面提供的可能性。

虽然 PostScript 是在 Linux 上生成要打印的文档的事实标准,但打印机本身不必理解 PostScript,PostScript 仍然是一项相对昂贵的技术。在许多情况下,尤其是在低端打印机上,PostScript 数据必须转换为打印机的本机页面描述语言。这是通过使用特殊的转换过滤器来完成的。一般来说,过滤器是一个特殊的程序,它将处理其输入并在其输出上生成处理后的数据。在 Linux 打印的上下文中使用了不同类型的过滤器:转换过滤器、I/O 过滤器(负责将数据传输到设备)、处理过滤器(转换文档数据)。

打印系统的基础是后台打印程序。后台打印程序管理打印作业队列。队列通常与单个打印机关联,用户提交的作业按先进先出的原则进行处理。当作业被处理时,其数据通常在到达打印机本身之前会经过一定数量的过滤器。UNIX 后台打印程序有许多不同的形式。我们将在此重点介绍在大多数 Linux 发行版中广泛存在的最流行的变体。

BSD LPD 打印系统

顾名思义,此打印系统源于 UNIX 的 Berkeley 发行版。线路打印机守护程序 (LPD) 仍然是许多其他打印系统和后台打印程序的基础,它们借用了其接口和配置文件格式,即 printcap 文件。虽然 LPD 最初是为与只能一次打印一行文本的线路打印机一起使用而开发的,但它也可以用于整页打印机。

这是在第一个完整的 Linux 发行版(如早期版本的 Slackware)中使用的打印系统。如今,许多发行版仍然附带此后台打印程序(Debian、Slackware),通常还附带本文中讨论的其他更现代的打印系统。今天仍然使用许多原始 BSD 后台打印程序的变体。

BSD 打印系统实际上只是一个后台打印程序 - 也就是说,它的核心功能仅限于排队作业。它由一个守护程序 (lpd)、/etc 中的几个配置文件(其中定义了队列及其属性)、一个后台打印目录(用于保存挂起的作业,通常是 /var/spool/lpd)以及一组基本的命令(用于提交、删除和操作作业 (lpq, lprm, lpc))组成。

队列在 /etc/printcap 文件中定义,该文件遵循与 termcap 文件相同的格式,termcap 文件用于描述 UNIX 终端的功能。典型的打印机条目如下所示

# Sample queue definition for BSD LPD
lp|printer1:
        :sd=/var/spool/lpd/lp:
        :lp=/dev/lp0:
        :if=/usr/sbin/somefilter:
        :mx#0:
        :sh:

每个条目定义一个队列。可以有多个队列指向同一物理打印机(例如,为了区分某些选项)。一个队列也可以有多个别名。在上面的示例中,队列 lp 有一个别名“printer1”。作业可以发送到这些打印机名称中的任何一个,并将被放入同一个队列。作为旁注,“lp”通常被认为是 BSD 世界中的默认队列。

作业通过 lpr 命令提交到后台打印程序。可以使用 -P 参数指定特定队列。例如

lpr -Pprinter1 /path/to/some/file

已提交但尚未处理的作业可以使用 lprm 命令从队列中删除。可以通过运行 lpq 命令获得作业 ID 号以及各种状态信息。

BSD LPR 非常重要,因为它还定义了 LPD 网络协议,该协议用于将作业提交到远程 LPD 守护程序,并允许 UNIX 工作站充当打印服务器。如今,几乎所有联网打印机都原生支持此协议。由于其广泛使用,所有其他打印系统都必须至少能够与其他 LPD 守护程序通信,从而实现此协议。

这是一个如何在 printcap 文件中定义远程队列的示例。作业将立即传输到远程 LPD 守护程序上的远程队列,并且不会在原始主机上处理。

# Sample queue definition for a remote LPD queue on a client
remote:
        :sd=/var/spool/lpd/remote:
        :rm=printserver.domain.tld:
        :rp=queue:
        :mx#0:

rm 属性指示远程 LPD 服务器的地址。rp 属性是作业将发送到的此服务器上队列的名称。

/etc/lpd.hosts 文件用于定义允许哪些主机将作业转发到本地 LPD 守护程序。

LPD 协议分两个不同的部分发送数据。首先,将构造并发送一个描述作业的控制文件。此控制文件包括有关始发用户、文件名以及附加到作业的任何选项的信息。然后,是数据文件 - 它是文档本身,其格式完全取决于当时使用的打印语言。

LPRng 打印系统

虽然 BSD 提供了 Unix 后台打印程序的基础,但其功能有限。启动了几个项目来改进它并添加更好的可配置性和更大的灵活性。如今,Linux 上最广泛使用的基于 BSD 的打印系统是 LPRng(下一代 LPR),由 Patrick Powell 编写。它本质上是原始 BSD LPR 系统的重写,但之前的所有概念仍然适用。

虽然 LPRng 保留了 printcap 文件格式,但它引入了许多新属性,使其配置更加灵活。过滤器定义可以分离,并且可以定义真正的 I/O 过滤器。用户还可以通过在其主目录中编写 .printcap 文件来定义自己的队列。

LPRng 还提供了模拟 UNIX System V 样式打印命令(lp、lpstat 等)的命令。

LPRng 附带 IFHP 过滤器,该过滤器可以与队列一起使用以自动执行某些数据格式转换(例如,打印 ASCII 文本或图像)。

通用 Unix 打印系统 (CUPS)

CUPS 是 Easy Software 启动的一个相对较新的项目。它从头开始设计,旨在取代基于 BSD 的打印系统,并集成了许多新兴标准和技术,使其成为非常前沿的打印系统。最近,CUPS 变得更加流行,因为 Apple 选择它成为 MacOS X 10.2 (Jaguar) 中的新标准打印系统。

CUPS 基于 Internet 打印协议标准 (IPP),IPP 是一个源自 HTTP 的 IETF 协议。CUPS 守护程序理解 IPP 请求,它是与其客户端应用程序通信的主要方式。作为一种 Internet 协议,IPP 可以轻松地在广域网上部署打印服务器。CUPS 还支持用于与打印机通信的其他流行协议,因此可以用作不具有本机 IPP 支持的联网打印机的桥梁。就像它所基于的 HTTP 一样,IPP 可以通过使用身份验证和 SSL 连接来保护。CUPS 为此提供本机支持,从而实现安全打印。

CUPS 采用的另一个标准是 PostScript 打印机定义文件格式 (PPD),这是另一个 Adobe 标准,用于描述 Postscript 打印机的功能。CUPS 将其用法扩展到非 Postscript 打印机,使其成为此架构中模块化驱动程序的基石之一。

CUPS 还使用许多过滤器作为将数据转换和传输到打印机的方式。但是,与基于 BSD 的后台打印程序不同,这是以更智能的方式完成的。CUPS 有不同类别的过滤器可用。

  • 后端过滤器提供最终数据将交付到的端点。有用于并行端口、TCP/IP 套接字连接、LPD 和其他端点的过滤器。

  • 文档转换过滤器与 CUPS 标准一起提供,可用于将图像、ASCII 文本、PDF 文件和 HP-GL/2 矢量文档转换为 Postscript。

  • 接口或栅格过滤器从 PPD 引用,并被调用以将文档从 Postscript 转换为中间文件格式。

与其他打印系统一样,需要一个转换过滤器才能打印到非 PostScript 打印机。CUPS 允许 PPD 文件描述用于转换为设备本机语言的过滤器,如下所示

*cupsFilter:    "application/vnd.cups-raster 0 rastertohp"
此示例来自 HP Deskjet 打印机的 PPD 文件。此行表示的含义是“rastertohp”程序(通常位于 /usr/lib/cups/filter 中的过滤器)将采用 MIME 类型为“application/vnd.cups-raster”的数据作为其输入,并将其转换为适合直接发送到打印机的格式,在本例中为 HP PCL 数据。此 MIME 类型是一种特殊的 CUPS 类型,表示栅格化数据,它基本上是一种原始位图格式,格式是 CUPS 过滤器可以理解的。CUPS 附带了 Ghostscript 的修改版本,该版本能够将 PostScript 转换为 CUPS 栅格数据:pstoraster 过滤器。上面一行中的数字 0 表示过滤器的“成本”,并且是 CUPS 用于确定过滤器链中过滤器优先级的值。

打印机类也由 CUPS 实现。最初是某些 System V 打印系统实现的功能,打印机可以分组为“类”,以实现自动负载平衡。类可以像常规队列一样发送作业。提交到类的作业将被分派到该类中第一个可用的打印机。

CUPS 的另一个简洁功能是其自动网络配置。使用广播协议,同一 LAN 上的所有 CUPS 守护程序相互通信,并且在服务器上配置的队列会自动浏览并在其他系统上可用。CUPS 还为在不同服务器上具有相同名称的多台打印机提供“隐式类”,从而提供自动负载平衡。CUPS 还支持 SLP(服务位置协议),某些设备可能会实现该协议以广播其存在。

在客户端,CUPS 同时具有类似 LPD 和类似 System V 的接口,这意味着它同时为系统提供 lpr、lpq... 以及 lp、lpstat 等命令。所有这些命令本质上都是通过 IPP 请求与 CUPS 守护程序通信的 IPP 客户端。

此外,CUPS 还附带一个基于 Web 的管理界面,允许管理员和用户直接从 Web 浏览器配置队列,或者只是检查其状态。这通常比使用神秘的命令或通过编辑 /etc/cups 中的 printers.conf 文件手动定义队列要用户友好得多。

CUPS 打印系统的最后一个特点是它不一定仅限于 PostScript 作为其输入数据。虽然可以认为其他打印系统也不一定有此限制,但 CUPS 使其更容易实现。我们之前已经看到如何在 PPD 文件中为 CUPS 指定转换过滤器,以及这如何涉及指定 MIME 类型。CUPS 广泛使用 MIME 类型来确定作业提交和打印机上的最终数据之间的数据流。过滤器可以在 *.convs 文件(通常在 /etc/cups 中)中定义,这些文件描述了每个过滤器程序:它们接受作为输入的数据类型、它们的“成本”以及它们输出的数据类型。给定某种类型的作业,CUPS 将智能地决定要调用的过滤器链,以便获得打印机接受的最终类型:PostScript,或 cupsFilter 行中 PPD 中指定的类型,正如我们之前看到的那样。

与应用程序的接口

如今,PostScript 语言仍然是 UNIX 世界中打印的主要接口。所有主要应用程序都将至少输出通用 PostScript,然后由打印系统处理,直到打印出来。这显然非常有限,因为应用程序没有统一的方式来查询打印功能,甚至不知道作业是否正确打印。很少有应用程序能够使用 PPD 文件来访问打印机功能,尽管 StarOffice 和 OpenOffice 是值得注意的例外。

但情况正在改善。例如,CUPS 提供了一个基本的 C API,允许应用程序更轻松地与其打印系统集成。此 API 包括通过 IPP 与 CUPS 守护程序通信的函数,以及读取和解析 PPD 文件并因此收集有关打印机及其功能的详细信息的函数。对于应用程序开发人员来说,这仍然非常有限,因为这仅适用于 CUPS 和类似的 IPP 服务器。

在自由软件方面,Gnome 和 KDE 桌面项目现在都包含中间层,以方便打印:KDEPrint 和 Gnome-Print。这些框架建议通过抽象底层打印系统,为应用程序提供统一的 API。

Linux 打印的未来

随着更高级的打印系统的出现,现在的情况比几年前好得多。由于这是一个对企业至关重要的问题,我们开始看到 HP 或 IBM 等知名供应商的支持,他们致力于改进此基础设施。

此外,自由标准组织正在开展 OpenPrinting 项目,其既定目标是为 Linux 操作系统定义下一代打印基础设施。该工作组汇集了来自行业的许多专家,正在定义 API 和标准,这将使 Linux 与其竞争对手齐头并进。

Stephane Peter 是 Codehost, Inc 在加利福尼亚州卡尔弗城的高级软件工程师。在不玩打印系统时,他可能会弹吉他或在南加州骑自行车。

电子邮件:speter@codehost.com

加载 Disqus 评论