文档已经出现在打印队列里,打印机却毫无动静。这段落差正好可以作为理解 CUPS 的入口:这套开源打印系统把应用程序的页面描述转换成打印机能够识别的数据,而接收请求、转换文档和交付数据分别属于独立的操作。[1] 跟随一项作业走过这些交接环节,就更容易查明打印机为何沉默。
本文沿用经典的 CUPS 2.x 架构,版本相关的行为以 2.4.19 手册为准。其过滤器与后端接口已标记为弃用;理解一套已经部署的系统,与为新项目选择开发接口,是两项不同的工作。[4][6]
纸页尚未产出,作业已经存在
系统的核心是调度器 cupsd。它接收 HTTP 和互联网打印协议(IPP)请求,管理打印目的地与作业,并启动外部进程来执行耗时较长的操作。调度器本身是单线程服务器,文档处理在它之外运行。[2]
假脱机目录通常位于 /var/spool/cups,其中存储着两类作业凭据。文件名以 c 开头的控制文件包含一条 IPP 消息,用于表示作业属性;以 d 开头的数据文件则保存提交的文档。每项已登记的作业都有一个控制文件,数据文件的数量可以是零个或多个。[2]
当有人说“CUPS 里还留着这项作业”时,这个区分就很重要。历史条目与留存的文档是两回事。2.4.19 配置手册中,PreserveJobHistory 控制作业记录的保留,PreserveJobFiles 控制打印结束后文档数据的保留。后者在手册中列出的默认值为 86,400 秒,即一天,实际部署时可以用配置覆盖这一默认值。[7] 历史记录仍在,供再次尝试打印的文档文件却未必还在。
队列开放接收,打印仍可停用
以一条名为 studio 的办公室打印队列为例。CUPS 分别报告队列是否接收提交,以及是否能够处理作业。只读命令 lpstat -a studio 查看接收状态;lpstat -p studio 查看打印机是否启用;lpstat -o studio 列出队列中的作业。[3]
因此,队列可以一边接收并积累作业,一边处于打印停用状态。提交成功后机器仍然闲置,这两种状态可以同时成立。应用程序已经完成提交,后续处理是否开始,还要另行确认。
对于只有一名管理员的小办公室,技术支持记录首先应保留这层区分,把队列名称、作业标识符和上述状态一起记下。笼统地写“打印机坏了”,会丢掉系统已经给出的信息;分别记录这三项观察,接手的人就有了调查依据。
格式转换沿着接口明确的处理链展开
CUPS 2.x 在 mime.convs 和其他 .convs 文件中描述可用的格式转换。每条规则都列出输入 MIME 类型、输出 MIME 类型、相对成本和过滤器程序。这里的成本描述资源开销,无法据此保证处理耗时或输出质量。[4]
设想提交文档的格式与目标设备要求的格式不同。要让一条转换链正常工作,相关过滤器必须已经安装,且彼此兼容。即使能够访问打印机地址,缺失的转换程序仍要补齐。反过来,即使已经生成有效的转换文件,设备是否可达仍需单独确认。
进程接口把这种分工落实到了具体操作中。第一个过滤器读取提交的文件,后续过滤器从标准输入读取数据,再向标准输出写入数据。后端程序位于处理链末端,负责把数据送往目的地。作业标识符、用户、标题、份数和选项都由参数传入。这些程序依据作业上下文运行,与提交用户的桌面之间没有交互连接。[5]
CUPS 还设有 FilterLimit,用于限制同时运行的过滤器的成本总和。在 2.4.19 中,设为零会关闭这项限制。[7] 对于共享打印服务器,排查延迟时应先了解这项调度控制,再判断延迟是否出在打印机上。同时仍需测量内存、CPU 和磁盘的使用情况;过滤器声明的成本与内存硬性上限是两个概念。
状态信息与页面数据各走各的通道
处理链的标准输出传递待打印的数据流,发给调度器的消息则经由标准错误通道传送,并带有规定的前缀:INFO: 更新状态消息,STATE: 调整持续保留的打印机状态原因,PAGE: 报告页数统计信息。[5]
诊断文字应写入对应的通道。一旦写进文档数据流,下游程序收到的内容就会随之改变。
取消作业也依赖各程序协作。作业被取消或挂起时,CUPS 会发送 SIGTERM;其编程指南要求辅助程序做好清理,留下有效的输出,或让设备处于已知状态。[5] 对于已经送出的纸张,取消操作无法让它沿滚轴退回。软件请求与物理过程各有自己的停止位置。
后端报告的结果有多个层次
后端接口约定区分了多种结果。CUPS_BACKEND_HOLD 挂起当前作业,CUPS_BACKEND_STOP 停止整个队列。通用的 CUPS_BACKEND_FAILED 则交由 printer-error-policy 决定取消、重试还是停止队列。这些结果影响的作业范围各不相同:暂停一份文档与停止一个共享打印目的地,属于不同的运维决策。[6]
CUPS_BACKEND_RETRY 允许稍后重试,其间可以穿插处理其他作业。CUPS_BACKEND_RETRY_CURRENT 要求立即重试,期间禁止其他作业插入。[6] 选择哪一种,会影响排在这份问题文档之后的所有人。
最能说明问题的是 CUPS_BACKEND_OK:手册将成功定义为数据已经传输到设备或远程服务器。[6] 要确认最后一张纸是否正确落入出纸托盘,还需要这项约定之外的证据。远程目的地增加了一段处理过程,实体打印机还要处理纸张。调查打印结果争议时,应记下确认接收作业的是哪个环节,并检查目的地留下的证据,再判断本地的成功状态是否足以证明纸面输出已经完成。
Stephane Peter 在 2003 年发表于 Linux Journal 的综述中,记录了更早时期用户遭遇的同类困扰:作业提交之后,用户很难清楚掌握它的去向。他把 CUPS 放在当时逐步改善的打印系统环境中加以介绍。[8] 从这套架构可以读出一层持久的诊断价值:作业的去向能够分段追查。留存的记录、已启用的队列、转换后的数据流,以及成功完成的交付,各自回答一个具体问题。把这些答案分别看清,一台看似沉默的打印机,也就成了一套有迹可循的系统。
来源
- OpenPrinting,《Overview of CUPS》——应用程序的页面描述、格式转换与打印职责。
- OpenPrinting,《CUPS Design Description》——调度器的组织方式,以及分开存储的控制文件与文档文件;保留设置以来源 7 的指定版本手册为准。
- OpenPrinting,《lpstat(1)》——查看队列接收状态、启用状态及队列作业的命令。
- OpenPrinting,《mime.convs(5)》,CUPS 2.4.19——指定版本的手册源文件,说明 MIME 转换规则、相对成本及弃用状态。
- OpenPrinting,《Filter and Backend Programming》——进程参数、标准流、状态报告和取消操作的处理。
- OpenPrinting,《backend(7)》,CUPS 2.4.19——指定版本的手册源文件,定义交付、挂起、停止、重试和成功等结果。
- OpenPrinting,《cupsd.conf(5)》,CUPS 2.4.19——指定版本的手册源文件,说明 PreserveJobFiles、PreserveJobHistory 和 FilterLimit。
- Stephane Peter,《Overview of Linux Printing Systems》,Linux Journal,2003 年 7 月 3 日——从独立视角记录当时对 Linux 打印系统与 CUPS 的认识。
- Raimond Spekking,《Xerox WorkCentre 6605 -7527.jpg》,2021 年 11 月 30 日,Wikimedia Commons——照片、设备型号确认及 CC BY-SA 4.0 署名信息。