Linux 在现实世界中的应用
一年前,我在家里的 386-20 上安装了 Linux,并在我的 Hercules 单色图形适配器上启动了 X11。我用 GNU emacs 编辑了一个源文件,并用 gcc 编译了它。一个真正的“Unix”系统在家中!我非常激动,连续几天都咧着嘴笑。“这太酷了!”我惊呼道。“这很好,但你用它来做什么呢?”这是当时的回复。那时我没有太多答案——但现在我有了。
本文介绍了一个现实世界中的 Linux 应用。需求是使用一台无人值守的计算机从三个不同的液位测量设备收集数据,并将数据中继到远程位置。
该地点是一个燃煤发电厂,距离计算机约 100 公里。这三个测量设备安装在一个高度和直径约为 7 米的直立圆柱形罐体上。罐体盛放水,水将与灰混合,制成比干灰更易于处理的浆液。罐体处有 120VAC 电源,但没有电话线。环境是良性的,除了持续存在的粉末状灰尘,类似于波特兰水泥和蛋糕粉的混合物。
测量设备都使用不同的串行协议和物理层。其中两个协议使用可打印的 ASCII 码,每个帧以 CR/LF 结尾。第一个协议(ASCII Modbus)类似于 RS-232 物理层上的 Intel hex 记录。第二个协议是一个专有命令接口,它在 RS-485 物理层上使用类似 shell 的命令。
第三个协议(RTU Modbus)由二进制数据组成,帧结束标志是大于 3 字节时间的间隙。物理层是半双工 FSK。计算机的接口是一个 RS-232 端口,连接到一个专有的 FSK 调制解调器。
为该系统选择的硬件是一台机架式工业 486 机器,配备 16M 内存和 500M IDE 磁盘驱动器。选择的工业 PC 具有一些对无人值守操作有用的功能
无需键盘即可启动的能力。
无需视频板即可启动的能力。
一个硬件看门狗定时器,可以在系统锁定时复位计算机。
由于没有电话线可用于在数据采集系统和中央主机之间提供通信,因此使用了蜂窝电话。
蜂窝通信——你只需要钱。
对于这项任务,我购买了一对 Microcom DeskPort 14.4K 调制解调器,它们支持 MNP-10 “蜂窝”功能集。蜂窝电话连接的质量每分钟的变化远大于陆地线路,并且掉线更频繁且持续时间更长。为了可靠的蜂窝通信,调制解调器需要能够重新均衡并相应地调整波特率和数据包大小。在禁用 MNP-10 功能的情况下,即使在 300 和 600 波特率下,我也无法保持可靠的连接。启用纠错后,连接通常保持在 9.6K 或 12K 波特率。
由于 UUCP 能够对工作进行排队,并在呼叫断线后自动重拨和重启传输,因此选择了 UUCP 而不是 SLIP。
蜂窝电话是一部摩托罗拉“手提包”式 3 瓦蜂窝电话,手头正好有,可以供使用。必须为电话购买一个“蜂窝连接”盒。蜂窝连接是一个黑色盒子,大约有一包香烟大小,插在手机听筒和无线电装置之间。它提供一个 RJ-11 插孔,产生拨号音,响应调制解调器的摘机挂机转换,并将 DTMF 音调转换为手机按键。
由于其中一个 ASCII 接口在 RS-485 物理层上运行,因此选择了 Opto-22 的一块板卡,该板卡具有标准的 16450 UART,带有光隔离的 RS-485 驱动器和接收器。
该系统需要无人值守的远程操作以及四个串行端口上的同时通信。虽然所有这些在 MS-DOS 下都是可能的,但这将需要大量的工作,而 Unix 类型的操作系统将“开箱即用”地支持所有这些。
Coherent 和 ISC SVr2 的副本可供使用,但我选择 Linux 有两个原因。首先,我当时(现在仍然是)在家中使用 Linux。更重要的是,Linux 源代码是可用的,以防需要定制或修复某些东西。
借来的 1993 年秋季 Yggdrasil CD 提供了基础系统,尽管 uucp 和邮件在安装后无法工作。我下载了 smail 和 Taylor UUCP 的新副本,它们安装和配置都没有问题。安装了 getty-ps,以便调制解调器可以被 uucp 用作拨出设备,也可以被用作远程登录的拨入设备。
第一项工作是看门狗定时器守护进程。看门狗守护进程需要定期执行 I/O 端口写入以复位硬件看门狗定时器。为了提供有序的系统关闭,当看门狗定时器守护进程收到 term 信号时,它会禁用定时器。由于端口地址高于 0x400,ioperm() 和 _outb() 系统调用将无法工作。
(内核仅维护 0x400 以下端口的权限映射。)相反,守护进程对 /dev/port 执行 open(),并使用 lseek() 和 write() 系统调用来执行端口 I/O。由于 I/O 操作量小且不频繁,因此系统调用开销不是问题。
下一项工作是通过三个串行端口收集数据的软件。它应该是一个与所有三个设备通信的单个大型程序吗?与编写三个独立的程序(每个程序都与单个设备通信)相比,这将是不必要地复杂的。尤其如此,因为这三个设备都使用了不同的协议并提供了不同的数据集。
这三个程序中的每一个都收集数据(每五秒钟一个样本),并在 stdout 上为每个样本写入一行文本。每行输出都包括时间戳、状态和数据值。每个程序都有一个命令行选项,用于指定在终止之前收集数据的时间长度。
简单的 ASCII 输出格式,带有空格分隔的列,可以使用熟悉的 Unix 工具(如 awk 和 gnuplot)轻松地进行操作和数据缩减。下面显示了一个数据文件中的几行
94-01-28 18:52:41 OK 0 4.745400 998.4952 94-01-28 18:52:47 OK 0 4.745406 998.4937
它就像一个廉价的免提电话——一次只能有一端说话。
与数据采集程序相关的唯一不寻常的问题是使用了半双工 FSK 调制解调器。当 Linux 主机发送命令时,必须断言 RTS,然后在允许设备响应时将其放下。这无法从用户级软件轻松完成,因此修改了串行端口驱动程序。在驱动程序中添加了两行代码,以便它在传输开始时断言 RTS,并在结束时放下它。您并不经常需要操作系统的源代码,但在这种情况下,它节省了大量额外的精力,否则需要添加自定义硬件来控制 RTS。
一旦各个数据采集程序被调试好,就需要一些东西来执行各个程序并协调整个过程。在 Unix 系统上,这意味着一个 shell 脚本:没有什么复杂的,只是一个无限循环,它执行以下操作
在后台启动三个数据采集程序中的每一个,并设置一个命令行开关,使其运行六个小时,并将 stdout 重定向到一个文件中。
等待上述所有三个程序终止。
压缩数据文件并将它们 uucp 到目的地。
这个 shell 脚本在 /etc/rc.local 中的一个条目中启动并在后台运行,每天四次发送数据文件。
事后看来,这一切听起来都很顺利,但该项目并非没有一些小小的波折。最令人尴尬的问题发生在尝试远程重启系统时。我输入了 shutdown -fh 而不是 shutdown -fr,因此系统停止了而不是重启。系统停机了一周,才有人可以到现场按下复位按钮。
连接到蜂窝调制解调器的拨入/拨出端口有时会被 getty 永久锁定。这阻止了 uucp 拨出以传输数据。添加了一个 crontab 条目来定期杀死该端口上的 getty。还有另外两次所有通信都丢失了。经过一些实验,我确定蜂窝电话不知何故被关机了。
查阅用户手册并致电服务提供商后得知,如果蜂窝电话在八小时内未使用,它将自行关机。这种情况发生了两次——显然蜂窝连接并不总是检测到调制解调器摘机状态,这导致蜂窝电话在八小时不活动后自行关机。UUCP 应该在八小时超时之前重试多次,因此确切的事件序列仍然有点神秘。直接的解决方案是配置一个 uucp crontab 条目,以确保即使没有任何工作要做,它也会每小时“打电话回家”一次。八小时超时可以禁用,当方便将电话送到商店时,将完成此操作。
在一个更平凡的方面,当我第一次尝试升级共享库以便运行较新版本的“man”实用程序时,我设法破坏了我的共享库。使用启动盘/CD-ROM 启动并修复库,使其能够再次从 IDE 驱动器启动,这是一项简单的任务。(当您升级共享库时,请在开始之前阅读两遍说明,并 完全 按照说明进行操作。)
Grant Edwards (edwar028@gold.tc.umn.edu) 是一位过程控制设备制造商的电气工程师。自 1980 年代初期以来,他一直在从事产品设计工作的同时摆弄 Unix 系统。