您能期待什么? —使用 Linux 的数据收集项目
我一直在我的 Debian 2.0 机器的系统时钟和硬件时钟上进行大量的测试和监控,这台机器我在家庭办公室网络上用于文件(Samba)、通信(ISDN/masq/diald)、打印和调制解调器池(mserver)服务。我想看看在不一直运行 ntpd 并保持 ISDN 线路连接到我的 ISP 的情况下,我使用 adjtimex 可以多好地校正系统时钟。我一直在使用 adjtimex 的日志记录功能以及 cron 和一个 Expect 脚本,每天记录时钟和一个 ntp(网络时间协议)参考服务器的数据。我注意到每天都有一些奇怪的变化,并且开始怀疑温度是否正在影响服务器的系统时钟。
adjtimex 允许您调整控制系统时钟的内核参数。文件 /etc/rc.boot/adjtimex 包含 TICK 和 FREQ 的设置,即用于调整主板上晶体振荡器频率变化的粗调和微调设置,晶体振荡器为系统时钟计时过程提供中断。命令
/usr/sbin/adjtimex --log --host ns.nts.umn.edu
将参考 ntp 服务器(在本例中为 ns.nts.umn.edu)、系统时钟和硬件时钟的数据记录到 /var/log/clocks.log。通过使用
adjtimex --review=/var/log/clocks.log您可以获得对 TICK 和 FREQ 的建议更改,这将调整内核时钟,并有望使其与 ntp 参考服务器匹配。只要时钟晶体稳定,这一切都很好;但如果它随温度变化呢?
如果我可以测量和记录服务器附近或实际内部的温度呢?然后我可以将温度数据与系统时钟数据关联起来,看看它们是否相关。我有一个带有串行接口的 Micronta (Radio Shack) 数字万用表。我收集数据所需要的只是一个将温度转换为电压并将仪表连接到服务器上串行端口的电路。
在网上稍微研究了一下,找到了一些热电偶到毫伏转换器,但它们的价格远高于我想支付的价格。作为一名电气工程师,并且在一家测量公司工作多年,我知道温度到电压转换器电路非常简单。几个朋友帮忙组装了一个电路,该电路每华氏度输出 0.01 伏电压,适合安装在一块大约一平方英寸的万能板上,并由 9 伏电池供电。有关原理图和图片,请参见图 1 和图 2。
Micronta No. 22-182 LCD 数字万用表配有一根五芯电缆和手册中的一小部分,描述了 MS-DOS 和 MS-BASIC 的测试程序。快速检查表明这些程序工作正常。串行接口通信参数为 1200 波特,7 个数据位,无奇偶校验和 2 个停止位。使用万用表的连续性测试功能,我绘制了如图 3 所示的电缆图,其中完整地标明了 PC 上串行端口信号的引脚排列和描述。这些端口具有 9 针或 25 针的公连接器,并接线为 DTE(数据终端设备)。RS-232 规范的设计使得 PC 上的 DTE 端口可以使用直通电缆连接到 DCE(数据通信设备),通常是调制解调器。DCE 通常具有母连接器。如果需要,您可以使用表 1 来连接 9 针到 25 针的转换电缆。现在我准备尝试在 Linux 上读取仪表。
在将电缆连接到 /dev/ttyS0 之前,我检查了端口上是否运行了 getty 或 gpm 等驱动程序。果然,有一个鼠标的 gpm 驱动程序残留,即使鼠标早已断开连接。我执行了
/etc/init.d/gpm stop
并使用以下命令重命名了 init 脚本
mv /etc/init.d/gpm /etc/init.d/nogpm以防止 gpm 在重新启动时重新启动。如果端口上运行了 getty 进程,您将必须通过注释掉 /etc/inetd.conf 中的正确行并使用以下命令重新启动 init 来禁用它
kill -HUP 1使用类似
ps auxw|grep tty之类的命令来确保您尝试使用的串行端口是空闲的。
我使用 stty 命令将端口设置为仪表的通信参数,命令如下
stty speed 1200 cs7 cstopb -echo clocal < \ /dev/ttyS0
speed 1200 设置波特率,cs7 设置 7 个数据位,cstopb 设置 2 个停止位,parity 默认设置为 none。我添加了 -echo 以确保设备驱动程序不会回显发送到端口的字符,并添加了 clocal 以禁用调制解调器控制信号。
我决定使用 Expect 来收集数据,因为仪表具有非常简单的“发送命令”和“获取响应”范例。Expect 是一个强大的工具,可用于自动化 UNIX 程序,这些程序与用户或需要命令或触发器并返回某种响应的进程进行交互。Expect 构建在 Tcl 之上,Tcl 是一种广泛使用的可扩展语言。我最近发现了 Expect,并发现它是那种一旦用上就不知道以前没有它怎么过来的工具之一。使用 Expect 可以轻松地自动化一些使用 shell 脚本或其他语言极其困难甚至不可能完成的事情。Sol Libes 的著作《探索 Expect》是一份宝贵的资源。Libes 先生是 Expect 的作者。我还发现 Expect 和 Tcl 网页非常有帮助。我之前曾使用 Expect 自动化了一些任务,例如上面提到的时钟数据记录。
仪表协议非常简单:向仪表发送 D\r(大写 D 后跟回车符),它会发回一个以 \r(回车符)结尾的 14 个字符的字符串。仪表发回的消息格式为
Byte 1 2 3 4 5 6 7 8 9 A B C D E Ex. 1 D C - 1 . 9 9 9 9 V \r Ex. 2 1 . 9 9 9 9 M o h m \r
在实践中,由于这是一个 3 位半的数字万用表,因此空格字符会替换第 9 列中的最低有效数字。
现在我已经清除了端口并将其设置为正确的通信参数,并且连接了电缆,我准备好与仪表通信了。
然而,当我将其连接到 Linux 盒上的串行端口时,我没有得到任何输出。幸运的是,我有一个串行分线盒,这是一种测试设备,每个信号都有一个双色 LED,开关用于断开信号,插座用于将它们跳接在一起。它插在计算机端口和您尝试诊断的设备之间。我的廉价分线盒在负电压时点亮红色 LED,在正电压时点亮绿色 LED。
经过多次探测和观察串行分线盒,我发现仪表依赖于保持 RTS(请求发送)信号为低电平,以为仪表的输出驱动电路提供负电压。如果没有 RTS 低电平,仪表的 TXD(发送数据)线路将无法工作。通常,当您打开端口时,RTS 和 DTR(数据终端就绪)线路都会变为高电平。
现在,如何在串行端口上控制调制解调器控制线?这就是访问串行驱动程序和其他实用程序的源代码真正有帮助的地方。如果这只是一个 DOS 应用程序(单用户,单任务),那么读取 ACE(异步通信元素)的控制寄存器,设置正确的位,并将数据写回端口就很简单了。由于用户空间程序无法直接写入系统设备,我必须弄清楚如何告诉设备驱动程序来操作 RTS 线路。经过大量搜索,我找到了一个 UNIX 串行支持站点,该站点链接到一个串行实用程序站点,该站点有一个实用程序,我可以对其进行破解以完成我想要的操作。我不是一个超级 C 程序员,但这正是我需要的,它为我提供了关于如何操作串行驱动程序的 ioctl 函数的线索。我编写了几个程序:clrrts.c 用于清除 RTS 线路,modctl.c 可以设置或清除串行端口上的 RTS 或 DTR。clrrts.c 和 modctl.c 的源代码可以在存档文件 ftp.linuxjournal.com/pub/lj/listings/issue68/3357.tgz 中找到。
在我早期的 Expect 会话中,我发现 Expect 和 cron 之间存在一个小问题。Debian 2.0 stable (libc6) 附带的 Expect 5.25 版本在由 cron 运行时不会生成进程。Debian 1.3 (libc5) 上的 Expect 5.19 工作正常。我向 Debian 维护人员报告了一个错误,得知 libc6 问题可能需要一段时间才能修复。我通过手动将 Expect 5.19 可执行文件和 Tcl 7.4 支持库从我的 Debian 1.3 系统安装到 2.0 服务器来解决这个问题,该服务器已经具有通用的 libc5 支持库,以支持我正在运行的另一个 libc5 软件包。
仪表具有自动关机功能,无法禁用。如果超过十分钟没有活动,它会关闭仪表。显然,这对长期数据收集不利。为了解决这个问题,我在 Expect 脚本中添加了一些代码来定义我希望每小时记录多少次数据,并将 cron 设置为每分钟读取一次仪表。这保持了仪表的开启状态,但避免了产生巨大的日志。运行 rddmm.exp 的 crontab 行是
* * * * * /usr/bin/expect5.19 /root/rddmm.exp
重新启动后出现了一些问题。我发现 Expect 脚本超时了,因为仪表没有响应。由此产生了两个问题。第一个是在您尝试更改某些 stty 参数时发生的一些有趣的事情,并且串行端口电缆没有连接到调制解调器控制输入线:CTS(清除发送)、DSR(数据集就绪)和 DCD(数据载波检测)。基本上,端口卡住了。由于仪表随附的电缆使 CTS、DSR 和 DCD 保持打开状态,并且我不想修改电缆,因此我弄清楚了哪些 stty 参数 不 能使用:hupcl 和 crtscts。在解决 RTS 低电平要求时,我已将 hupcl 放在端口的原始 stty 设置中。端口已接受 hupcl 设置,因为在发出命令时,我在端口上安装了串行分线盒,并使用跳线绕过了调制解调器控制信号。但是,当仅将仪表电缆连接到端口时,缺少反馈信号 CTS、DSR 和 DCD 导致 hupcl 挂起端口。这直到重新启动才显示出来。
其次,我需要在串行端口上设置“原始”模式,默认情况下启动参数设置为“cooked”,这会将回车符转换为换行符。这阻止了 Expect 脚本看到响应末尾的 \r。这些更改也已合并到 Expect 脚本中。
Expect 脚本 rddmm.exp(带有编号的行以供参考)包含在存档文件中,以及代码的逐行解释。
温度转换电路和万用表都使用 9 伏电池供电。由于我想连续数周采集数据而无需担心它们没电,因此我设计并构建了几个简单的电源,使用可调电压调节器和插入墙壁的立方体变压器作为电池消除器。这些也安装在一块略大于一平方英寸的万能板上。(见图 4。)
最后一部分是数据可视化。我使用 gnuplot 读取日志文件并绘制温度随时间变化的曲线图。我以前没有使用过 gnuplot,但是花了几个小时阅读手册页,我就可以在 Linux 控制台上查看该图,或将其打印到我的 HP LaserJetIII。
/var/log/temps.log 中的行看起来像这样
Dec 31 10:45:01 server1 rddmm: 68.9 Degrees F
可操作的 gnuplot 指令是
set xdata time set format x "%b %d\n%H:%M" set title "Internal Server Temperature at Timekeeping Crystal" set timefmt "%b %d %H:%M:%S" set xrange [ "Jan 03 14:00:00" : "Jan 04 07:59:00"] set ylabel "Degrees F" -2 plot "/var/log/temps.log" using 1:6 with lines 1xdata 和 timefmt 指令告诉 gnuplot 水平轴以时间为单位测量,以及日志文件中的时间格式。xrange 指令确定要绘制日志文件中的哪些行。format x 指令定义 x 轴上的标签;日期和时间之间的 \n 强制使用双行标签。plot 命令告诉 gnuplot 在哪里找到日志文件,要绘制哪些列以及使用折线图样式 1。set title 和 set ylabel 在图上放置标题和 y 轴标签。
要将图打印到激光打印机,我使用了
set terminal pcl5 set output '/root/plot.out' replot
然后从 shell
lpr /root/plot.out以类似的方式,我使用此处显示的 gnuplot 指令绘制了系统时钟时间测量值与 Internet 上的参考 NTP 服务器之间的差异
set xdata time set timefmt "%Y-%m-%d %H:%M" set xrange ["1999-01-03 15:00":"1999-01-04 07:00"] set format x "%b %d\n%H:%M" set title "Delta sysclock Minus Delta refclock" set ylabel "Seconds" -2 plot "/root/clk_hr.prn" using 1:3 with lines 1
现在,经过这一切,系统时钟是否受到温度的影响?通过查看图 4 和图 5,您可以看到时钟差异的变化与服务器内部的温度变化并不一致。事实上,存在一个很大的时间变化,它自行纠正了,我必须将其归因于网络延迟的变化。通过每小时而不是像我最初那样每天获取一次时间数据,可以更容易地识别随机网络变化,这最初引起了我的好奇心。
