使用 MetaCard 的图形应用程序
在像 Linux 这样的多任务操作系统上,拥有一个 top 程序是非常有用的。您可以使用它来跟踪系统上所有程序的 CPU 和内存使用情况,并检测和终止失控进程。Linux 系统自带的基于字符的 top 程序可以得到改进。因为它没有意识到窗口系统,所以当它运行所在的窗口被最小化时,它不会进入休眠状态。如果您可以通过点击一个进程来选择要终止的进程,而不是让 top 输入其进程 ID,那也会很好。
幸运的是,Linux 有一种非常简单而优雅的方式来获取进程信息,因此很容易开发一个图形应用程序来使用像 MetaCard 这样的工具来显示这些信息。本文将向您展示如何使用 MetaCard 的脚本语言 MetaTalk 来完成这项工作。MetaTalk 是一种非常高级的语言 (VHLL),它支持以非常少的努力构建完整的应用程序。MetaTalk 语言具有类似英语的语法,并且非常简洁。这使得它易于学习,但又不牺牲在其他 HVLL(如 Perl)中发现的功能和紧凑性。它非常易读,因此非专业用户很容易弄清楚脚本的作用(对于像 Perl 这样的语言来说,这可能要困难得多)。
在 Linux 系统上运行的每个进程都在 /proc 中有一个目录。该目录中有几个文件包含有关该进程的信息。为了实现图形化的 top 程序,我们最感兴趣的是名为 stat 的文件中的信息,该文件包含进程运行时和内存使用信息。我们还将使用名为 cmdline 的文件,其中包含用于启动该进程的命令行。
stat 文件包含一行信息,字段之间用空格分隔。有关格式的详细信息,请参见 proc 手册页。此页面上的信息表明,在 Linux 1.2 系统上,第 14 和 15 个词分别是特定进程的用户和系统运行时间。第 23 个词是以字节为单位的进程大小,第 24 个词是该进程当前在 RAM 中的 4KB 页数。
在 /proc 系统中还有一些文件包含有关整个系统的信息。我们将使用 /proc/stat 文件,该文件包含整体系统资源使用情况。我们需要该信息来计算每个进程的 CPU 使用百分比。
每次要更新显示时,程序必须执行以下操作
读取 /proc 目录中的 stat 文件。
查找 /proc 目录中的所有子目录。
读取每个子目录中的 stat 和 cmdline 文件。
通过从上次时间中减去来计算进程的 CPU 时间。
保存当前的 CPU 使用率。
将 CPU 使用率转换为总使用率的百分比。
构建进程列表并显示它。
安排时间重新进行更新。
执行所有这些操作的 MetaTalk 处理程序称为 updatelist,如 列表 1 所示。此处理程序与所有 MetaTalk 消息处理程序一样,以单词 on 和要处理的消息的名称开头。此处理程序的前几行声明了此处理程序中使用的所有局部变量。虽然不是绝对必要,但声明变量以避免因拼错变量名而导致的错误是一个好主意。要检查您的脚本,您可以设置 MetaCard 属性 explicitVariables,这将把任何在声明之前使用的变量标记为错误。
然后,处理程序从文件 /proc/stat 中获取全局系统时间统计信息,并从上次调用处理程序时减去这些值。然后将时间统计信息存储在处理程序外部声明的局部变量中,这类似于 C 中的“静态”变量。也就是说,它像全局变量一样保留其值,但只能在脚本中引用,因此它不会污染全局命名空间。此变量与所有 MetaTalk 变量一样,可以用作关联数组,而无需特殊声明。您只需在 [](方括号)之间放置一个字母数字字符串即可。
请注意 “word x of y” 形式的表达式。这些被称为“块”表达式,是 MetaTalk 的一个非常强大的功能。使用它们,您可以单独访问字符串的元素,而无需先将整个字符串拆分为数组。另请注意,您可以将单词相加,而无需先将它们显式转换为数字。这节省了开发时间,并使脚本比用低级语言编写的脚本更小。
updatelist 处理程序中使用的 readfile 函数如 列表 2 所示。与所有函数处理程序一样,它以关键字 function 开头,并在表达式中像 MetaCard 内置函数一样使用。
此函数有一个不寻常的地方。通常在 MetaCard 中,人们会使用
read from file x until eof
因为当您将 eof 指定为终止条件时,MetaCard 通过获取文件大小并使用单个系统调用读取整个文件来加速处理。但这不适用于 /proc 文件,因为 stat() 系统调用返回的文件长度为 0 字节,即使它们包含数据。因此,我们必须通过指定 empty 作为终止条件来强制 MetaCard 一次读取一个字节的文件。
readfile 函数还必须处理指定的文件不存在的情况,这可能发生在执行获取目录内容的 ls 命令后进程退出时。它还需要将字符串中的空字符(ASCII 0 字符)转换为空格,因为某些文件使用此字符作为分隔符。请注意,这需要一种可以处理二进制数据的脚本语言。MetaTalk 对此没有问题,但许多其他脚本语言都有问题。
updatelist 处理程序的其余部分执行上述步骤 2 到 7。生成的列表被排序两次,以纠正基于字符的 top 程序更令人讨厌的特性之一:如果单个进程不使用任何 CPU 时间,它们会在列表中不可预测地跳动。相反,我们将按进程大小排序,然后再按 CPU 时间排序,这是一种更有用的方式。这之所以可能,仅仅是因为 MetaCard 的排序是稳定的,这意味着如果元素具有相同的键值,则顺序会在连续排序中保留。
最后,updatelist 处理程序使用 send 命令在几秒钟内安排回调自身,并将 send 创建的计时器的 ID 存储在一个变量中。可以使用此局部变量使用 cancel 函数取消计时器。例如,当应用程序被最小化时,我们希望停止处理(参见列表 3)。
我在 send 命令中偷偷加入了一些面向对象的编程。发送到 “me” 的消息是当前正在执行其脚本的对象。时间间隔被指定为 “the update interval of me”。此对象具有名为 updateinterval 的自定义属性,该属性是持久性的,这意味着它会在每次保存对象时保存。这就是为什么您在这些列表中看不到此属性的任何初始化;它是与堆栈一起存储的数据,而不是代码。
稍微回溯一下,在编写脚本之前,您需要创建一个对象来附加脚本。MetaCard 应用程序由一个或多个堆栈组成,每个堆栈都有一张或多张卡片。隐喻(继承自 HyperCard)是一叠索引卡片,但您也可以将其视为书中的页面、演示文稿中的幻灯片或电影或视频中的帧。堆栈存储为类似于 Mac 和 Windows 上使用的资源文件的二进制文件。但除了对象描述之外,按钮状态和字段中的文本等数据也存储在这些文件中。
对象是使用 MetaCard 的 IDE(集成开发环境)创建、调整大小和定位的。要开始构建新的应用程序窗口,请从“文件”菜单中选择“新建堆栈”(第一张卡片会随着堆栈自动创建)。然后从工具栏中选择工具,并绘制您需要的字段和按钮。有关 MetaCard top 应用程序的屏幕快照,请参见图 1。
在创建堆栈和其中的控件后,您可以向堆栈、卡片和/或控件添加脚本。通常,每个对象都有自己的脚本,但我已将 top 程序的所有处理程序都放在卡片脚本中。这意味着 mouseUp 处理程序(参见 列表 3)有点不寻常,因为它通过消息传递层次结构被调用:未被对象处理的消息可以由层次结构中更高的对象处理。在这种情况下,卡片脚本中的处理程序处理发送到卡片上任何控件的消息。
编写一个大的处理程序而不是一堆小的处理程序通常意味着您必须弄清楚消息最初发送到哪个对象。target 函数提供此信息。target 函数返回一个对象,您可以获取该对象的属性或向其发送消息。此 mouseUp 处理程序还展示了 MetaCard switch 控制结构
当用户单击“终止进程”按钮时,将执行“终止进程”案例。此部分从字段中选定的行获取进程的 PID,并使用 MetaTalk kill 命令终止它。
“设置更新间隔...”部分使用 ask 对话框提示用户输入新的 updateinterval 值,验证该值是否为数字,并告知用户如果不是数字则必须是数字。ask 和 answer 对话框内置于 MetaCard 中,是快速简便地从用户那里获得简单响应的方式。如果新数据检查正常,则处理程序取消当前计时器,然后调用 updatelist 处理程序以重新启动它。
当用户单击主字段中的一行时,将执行 “toplist” 案例。此案例启用 “终止进程” 按钮并抑制更新 5 秒钟,让用户有时间在字段下次更新时清除选择之前终止进程。
列表 4 显示了面向堆栈的消息的处理程序,包括堆栈打开和关闭以及最小化和取消最小化时发送的处理程序。请记住让应用程序在最小化时进入休眠状态的目标。
MetaCard 没有基于约束的几何系统,因此您必须编写脚本来处理在堆栈窗口调整大小时调整大小和重新定位控件。resizeStack 消息处理程序执行此几何管理,如列表 2 所示。使用 IDE,我设置了堆栈属性,使堆栈在宽度上不可调整大小,只能在高度上调整大小(因为您需要能够看到所有字段)。因此,此处理程序只需垂直调整主字段的大小,并沿显示屏的底部边缘重新定位按钮。这个简单的四语句处理程序可靠地处理了该任务,而无需触发使基于约束的系统正常工作所需的耗时的反复试验开发阶段。
运行 MetaCard 版本的 top 所需的 CPU 时间大约是基于字符的版本的两倍(在运行 Linux 1.2.13 的 Pentium 90 上为 6% 对 3%)。这是一个典型的结果,因为一个编写良好的 MetaCard 脚本通常比一个可比的 C 程序慢 2 到 4 倍。当然,MetaCard 版本只用了很短的开发时间。而且因为它小得多,所以维护和定制它将花费更少的时间和精力。基于字符的 top 程序是用 C 语言编写的,长度约为 10 倍。MetaCard 版本的 top 的内存使用量远小于基于字符的 top 程序加上运行它所需的 xterm 和额外的 bash 进程所需的内存总和。
这个图形化 top 的真正强大之处在于它很容易修改以满足您的需求。您可以轻松地添加列来显示基于字符的 top 中显示的其他一些信息,或者更改用于终止进程的信号。由于 MetaCard 具有内置的对象图形功能,您甚至可以添加图形显示,以显示各个进程随时间推移使用的资源。
您可以从 Linux Journal 的 ftp 站点 获取 MetaCard top 堆栈,但您需要从 MetaCard WWW 站点 http://www.metacard.com 或 FTP 站点 ftp://ftp.metacard.com/MetaCard/ 获取 MetaCard 引擎。
Scott Raney (raney@metacard.com) 是 MetaCard Corporation 的总裁,该公司是一家基于高级语言和直接操作界面的多媒体和 GUI 开发工具供应商。