Linux 任务调度

作者:Michael A. Schwarz

今天,在我们持续进行的关于学习如何与 Linux 的“内部守护进程”共处的系列文章中,我们将探讨两个在 Linux 上调度任务执行的守护进程。这些守护进程或多或少与几乎所有 UNIX 系统上的守护进程完全相同。(Linux 为 atcron 提供了单独的守护进程。旧版本的 Linux 使用一个名为“atrun”的程序,该程序每分钟在 root 的 crontab 中运行一次,以执行 at 请求。其他一些 Unix 操作系统直接在 crond 中具有 atd 功能。此限定符由作者诚实局带给您。本文将介绍 atd 和 crond,因为它们随目前销售的大多数发行版一起分发,包括 Debian 2.1、Red Hat、SuSE 和 Corel 等。)。我的测试用例都是在 Red Hat 6.1 安装上使用 at 版本 3.1.7 进行的。我目前拥有的 Debian 和 SuSE 版本是 at 3.1.8。

至于 cron,大多数 Linux 发行版使用“Vixie cron”,顾名思义,它最初是由 Paul Vixie 编写的。各个发行版都进行了自己的修复,以解决 1999 年 8 月发现的安全漏洞。查看您的发行版的更新页面以获取最新版本的 cron,并确保您已安装它。

您对 at 和 cron 的看法很大程度上取决于您的背景。如果您只熟悉 DOS 和 Windows 世界,您应该会对 atd 和 crond 提供的功能印象深刻,即使您使用过与 crond 有某些相似之处的 System Agent。如果您是来自 MIS 世界的老手,在那里您拥有 JCL 和各种批处理环境控制系统,您可能会发现 atd 和 crond 缺少一些基本功能。即便如此,我希望您通过这篇介绍,对这些工具提供的功能有一个健康的认识,并可能有一些关于如何利用它们的局限性来显着增强 Linux 功能的想法。

具有大型机背景的人非常熟悉任务调度的概念。他们通常将此术语与批处理互换使用。唉,任务调度不是批处理。至少在我看来,批处理包括任务依赖性、批处理过程监控、检查点/重启和可恢复性等概念。atd 和 crond 都不提供这些功能。如果您来自大型机世界,您可能会感到有些失望。不要。正如您将看到的,atd 和 crond 非常符合 UNIX 的整体理念,即简单的工具做好一件事。

如果您来自 Windows/DOS 角度,您应该会对 atd 和 crond 的多用户性质感到满意。与 System Agent 不同,您不必登录即可执行您的任务。

如果您有 UNIX 背景,那么,您就像回到了老朋友中间。

对于那些完全不熟悉这些概念的人,我们正在谈论的是运行程序。那又怎样,您说?我登录并键入命令并单击小图标。我整天运行程序。有什么大不了的?

让程序在一天中的某个特定时间运行怎么样,无论您是否在那里?在繁忙的 Linux 服务器上编译最新版本的 WINE,而不会降低分支机构内网的速度怎么样?在线订单应用程序吐出的烦人的日志文件即将耗尽 /usr/prod/orders 上的所有可用磁盘空间,这又怎么样呢?

这就是任务调度发挥作用的地方。

计划任务有两种。您可以将它们视为“一次性”和“重复性”。一次性任务是您希望在未来某个时间执行的程序的单次执行,无论您是否登录。重复性任务是您希望在特定时间或日期一遍又一遍运行的程序。

您用于调度一次性任务的命令称为“at”。调度重复性任务的方法是通过“crontab”(这是一个由 CRON TABle 组成的混成词,类似于 INITtialization TABle 和其他 *nix-y 混成词)。奇怪的是,用于查看、编辑和存储 crontab 的命令也称为“crontab”。

与我们在本系列中介绍的其他一些守护进程不同,这两个守护进程具有控制它们的交互式用户程序。因此,我们将介绍作为非特权用户使用这两个守护进程的基础知识(我希望您没有以 root 身份登录到您的 Linux 系统!),然后我们将介绍守护进程及其工作原理,然后我们将介绍“非用户”或系统计划任务的一些要点,最后介绍一些有时会导致命令在通过调度程序运行时表现与您预期不同的“陷阱”。

使用 at

at 命令用于计划一个或多个程序在稍后时间单次执行。实际上有四个客户端命令

  1. at:在指定时间运行命令

  2. atq:列出待处理的命令

  3. atrm:取消待处理的任务

  4. batch:当系统负载允许时运行命令

Linux at 命令接受多种时间规范,大大扩展了 POSIX.2 标准。这些包括

HH:MM
在此时和分运行。如果此时已过,则假定为第二天。除非您在时间后附加“am”或“pm”,否则假定为 24 小时制时间。
now noon midnight teatime
您没看错。您可以键入“at teatime”,Linux 的 at 非常人性化,足以知道这是当地时间下午 4 点。“noon”和“midnight”关键字具有其正常含义。“now”关键字的意思就是字面意思。这似乎是一个很愚蠢的功能,因为如果您想立即运行某些东西,您无需使用 at 命令即可键入它,但它在“相对时间”调用中具有应用。我们将在下面描述的日期修饰符之后看到这些。
日期修饰符

这些时间规范可以选择后跟日期规范。日期规范有多种形式,包括

today tomorrow

这些意味着您期望的。 “at teatime tomorrow”将在第二天下午 4 点运行命令。请注意,如果您指定的时间已过(例如,当时间为下午 3 点时,“at noon today”),则任务将立即运行。您不会收到错误。乍一看,您可能会认为这是一件坏事,但请从这个角度来看。如果系统自上午 10 点以来一直停机,并且现在在下午 3 点才重新启动怎么办?您希望跳过关键任务,还是希望它尽快运行? at 系统采取保守的观点,并假设您希望运行该任务。

<month_name> <day> [<year>]
其中 month_name 是“jan”或“feb”等,day 是日期编号。年份是可选的,当然应该是四位数的年份。
MM/DD/YYYY YYYY-MM-DD
不要听“man at”页面告诉您的!至少在 Red Hat 6.1 中,它是错误的!我怀疑它在某些其他版本中也是错误的,我敢打赌这是因为文档没有跟上此子系统的 Y2K 修复。 Red Hat 6.1 附带的 at 处理上述两种格式的日期。它似乎可以正确处理两位数年份,将小于 50 的值转换为 20xx,将大于 50 的值转换为 19xx。我没有测试以找到确切的支点,我也不建议您这样做。如果您此时使用两位数年份,请准备好付出代价!依赖于您的 at 版本以某种方式处理两位数年份是愚蠢的。使用四位数年份。我们还没吸取教训吗? (如果您在 1995 年到 1999 年期间从事计算机工作,您会感受到痛苦,因为当我们在显微镜下仔细检查每个系统,寻找我们系统设计中的日期缺陷时,工作几乎完全停止了。不要制造 Y2.1K 问题!拜托!!!)
相对时间

您可以修改时间规范的另一种方法是对其应用相对时间。相对时间规范的格式为 + <count> <time units>,其中“count”只是一个数字,“time units”是“minutes”、“hours”、“days”或“weeks”之一。

所以,您可以说

at 7pm + 2 weeks

程序将被安排在从今天起两周后的当地时间晚上 7 点运行。

最常见的形式之一是

at now + x units

指定一个或多个程序在从现在起多少个单位后运行。我经常使用它来关闭我在家里的机器从工作场所拨号上网的连接。我在上班前拨号上网,然后在妻子回家前将其关闭(我太吝啬而不想买第二条线路)。我使用 ssh 从工作场所登录,并且我喜欢干净地关闭所有窗口,所以我经常这样做

# ps fax | grep wvdial
  599 ?        S      0:00      \_ wvdial
  875 pts/2    S      0:00      \_ grep wvdial
# at now + 10 minutes
at> kill 599
at>
warning: commands will be executed using /bin/sh
job 9 at 2000-04-17 16:30
# exit
$ exit
然后我有十分钟时间从我的家庭系统干净地断开连接,然后我的电话连接就会断开。
运行时环境

请注意,旧式的 Bourne shell 用于 at 运行的所有命令。(另请注意:我必须键入 ctrl-d,*nix EOF 字符才能关闭交互式 at 会话。有关更多信息,请参阅关于 at 命令行部分。这只是影响 at 计划命令行为的一个因素。以下是一些需要记住的其他事实。当发出 at 命令时生效的当前工作目录、环境变量(有三个例外,请参见下文)、当前用户 ID 和 umask 将被保留,并在执行命令时使用。三个环境变量例外是 TERMDISPLAY 和“_”(通常包含 shell 中执行的最后一个命令)。命令的输出将通过邮件发送给发出 at 命令的用户。如果 at 命令在 su shell 中发出(意味着,如果您“成为”另一个用户),则输出邮件将发送给登录用户,但程序将在 su 用户下运行。

权限

使用 at 的能力由两个文件控制:/etc/at.deny 和 /etc/at.allow。

首先检查 /etc/at.allow 文件。如果该文件存在,则只允许该文件中的用户名运行 at。如果 /etc/at.allow 文件不存在,则检查 /etc/at.deny 文件。所有在该文件中提及的用户名都可以运行 at。

如果这两个文件都不存在,则只有超级用户可以运行 at。

命令行

at 命令运行通过标准输入传递的命令(通过管道传入,或在“at>”提示符下键入,如上面的示例所示),或者运行由 -f 参数命名的文件中指定的命令。

at 命令行的通用形式是

at [-V] [-q <queue>] [-f <file>] [-mld] <TIME>

其中“queue”是队列名称。队列名称是字母,a-z 或 A-Z。有关更多详细信息,请参阅名为“队列”的部分。

“file”是包含要运行的命令的文件的名称。

“TIME”是如上详细讨论的时间规范。

其余开关是 -m(在任务完成时向用户发送邮件,即使未产生任何输出); -l(atq 的别名。请参阅下面的 atq 部分); -d(atrm 的别名。请参阅下面的 atrm 部分)。

atq 命令

atq 命令列出当前用户排队的任务(除非以超级用户身份运行,在这种情况下,将列出所有用户的待处理任务)。

这是一个示例

mars:20:~$ atq
5       2000-06-20 15:00 a
6       2000-07-04 15:00 a
10      2000-04-24 14:33 f
mars:21:~$

第一列是任务编号,后跟计划运行时间,后跟队列。在本例中,两个任务在队列“a”中,一个任务在队列“f”中。有关更多信息,请参阅关于队列的部分。

您可以使用 -q 开关仅查看特定队列中的任务。

atrm 命令

atrm 命令用于从 atq 中删除任务。例如,考虑上面 atq 示例中的队列。以下会话说明了 atrm 的用法

mars:21:~$ atrm 6
mars:22:~$ atq
5       2000-06-20 15:00 a
10      2000-04-24 14:33 f
mars:23:~$

您可以在命令行中列出任意数量的任务编号。

batch 命令

batch 命令是 at 的变体,它不是为将来的某个时间调度任务,而是立即提交任务,但该任务在系统负载平均值降至 0.8 以下之前不会启动。什么是负载平均值?最简单的理解方式是等待运行的进程数。大多数时候,程序处于空闲状态,等待硬件或输入,或等待内核完成请求。当程序实际上有事要做时,它处于可运行状态。如果系统不忙,内核通常会立即将控制权交给此类程序。当另一个程序正在运行时,刚变为可运行状态的程序必须等待。瞬时系统负载是未运行的可运行进程的数量。负载平均值是短时间内瞬时负载的平均值。因此,负载平均值低于 1.0 的系统有一些空闲时间。负载平均值在 1.0 左右徘徊的系统完全繁忙,并且处于理论最大容量。负载平均值超过 1.0 的系统没有空闲时间,进程正在等待运行机会。请注意,这并不一定意味着系统对用户来说会明显变慢,但这确实意味着系统已达到最大容量,并且程序运行速度比在不太繁忙的系统上可能的速度慢。

batch 命令为“立即”调度任务,但会延迟任务的启动,直到系统有空闲时间(负载平均值低于 0.8)。请注意,此测试用于启动任务。一旦启动,它将运行到完成,无论系统在运行期间变得多么繁忙。

队列

请注意,本节主要针对 Linux。我使用过的其他 UNIX 操作系统也有队列,但它们与此处记录的队列不同。始终查阅本地文档。例如,AIX 不以这种方式工作。

队列是将任务分组到单独列表中的一种方式。它们以 a-z 和 A-Z 命名。 at 命令默认将任务放在队列“a”中,而 batch 默认将任务放在队列“b”中。

具有“更大”值的队列名称以更高的“nice 值”运行。 Nice 值是 Linux(和其他 UNIX 系统)设置任务优先级的一种方式。任务的默认 nice 级别为“0”,表示“正常”。任务的 nice 值可以从 -20(最高优先级)到 +19(最低优先级)。只有超级用户才能为任务赋予负 nice 值。我们在此不再赘述 nice,因为对内核调度程序的讨论远远超出了我们的范围。只需知道,“z”队列中的任务以较低的优先级运行(因此速度较慢,对其他正在运行的任务的影响也较小),而“a”队列中的任务则不然。

正在运行的任务将在“=”队列中,该队列保留用于正在运行的任务。

队列名称区分大小写!请记住,有 a-z 队列和 A-Z 队列。 A-Z 队列是特殊的。如果您使用 at 将任务放在带有大写字母的队列中,则该任务将被视为在运行时提交给 batch 命令,而不是 at 命令。

换句话说,将任务放在大写队列中就像结合了 at 和 batch。当任务运行时,如果负载平均值低于 0.8,它会立即运行,否则它会等到负载平均值降至该点以下。在任何情况下,任务都不会在其计划时间之前启动。

唷!说了这么多,我们仍然没有看过处理所有这些的守护进程!我希望您开始明白,“at”虽然不是一个完整的批处理系统,但肯定提供了很大的功能。

它们如何工作

at 和 batch 命令将任务放入 at 队列。什么是 at 队列?嗯,有一个目录 /var/spool/at,只有守护进程用户和超级用户可以访问(一切都对超级用户可用)。对于每个任务,目录中都有一个文件。该文件是一个 shell 脚本,用于设置环境和 umask,cd 到工作目录,然后依次运行为 at/batch 指定的程序。

命令完全按照键入/通过管道传递到 at 的方式进入 shell 脚本。每个命令依次运行。如果您使用 &&&; 将任务放入后台,或使任务相互依赖,这些将被遵守。

重要提示!shell /bin/sh 用于运行这些任务。如果您通常使用其他 shell,例如 tcsh,请注意您不能使用该 shell 的语义,因为将改为使用 /bin/sh。

atd 守护进程

此时,记录守护进程有点虎头蛇尾。 atd 守护进程检查 /var/spool/at 目录。文件的名称实际上编码了它们的运行时、队列和 batch 与 at 状态。这些文件是 shell 脚本,用于设置环境并如上所述运行任务。任务的输出临时存储在 /var/spool/at/spool 中,直到任务完成,之后输出将通过邮件发送给调用用户。

At 总结

at 不如 cron 广为人知,但在许多方面是更强大的工具。

使用 crontab

系统上的每个用户都可能有一个 crontab,这是一个由 CRON TABle 组成的混成词。用于创建、检查和修改 crontab 的命令称为 crontab。

有四种调用 crontab 的方法。

crontab <file>
crontab -l
crontab -r
crontab -e

通常,crontab 适用于您自己的 crontab。所有四种形式都接受 -u 选项,后跟用户名。在大多数情况下,只有当您是超级用户时,才能查看和编辑其他用户的 crontab。如果您能够编辑其他用户的 crontab,您可能需要检查您的系统安全性。您可能有一些问题!

第一种形式将命名文件存储为 crontab,替换任何当前的 crontab。第二种形式将当前 crontab 转储到 stdout。第三种形式删除当前 crontab。第四种形式在 VISUAL 或 EDITOR 环境变量指定的编辑器中打开当前 crontab。

如果您想尝试使用您的 crontab,最好执行

crontab -l working-crontab

保存您当前的 crontab(如果有),然后使用

crontab -e
在您最喜欢的编辑器中修改您的 crontab。您可以随时使用
crontab -r working-crontab
将一切恢复原样。

此时,您可能想知道 crontab 是什么样子的以及它的作用是什么。

基本 crontab 格式

crontab 是程序命令行列表,以及何时运行该命令行的规范。它是一个以空格分隔的文件,命令之间有换行符。空白行和以磅字符 (#) 开头的行将被忽略。

字段是

minute  hour   day of month    month    day of week    command

任何时间字段都可以是星号 (*),表示“每”。因此,一个条目

* * * * * fetchmail
将每分钟运行一次 fetchmail,每天每小时的每分钟都运行。

允许数字范围。所以

* 8-17 * * 1-5 fetchmail

将在周一至周五的上午 8 点到下午 5 点之间每分钟运行一次 fetchmail(0 或 7 代表星期日)。

允许列表。因此

0,20,40 * * * 1-5 fetchmail

将在周一至周五每天每小时的整点、20 分和 40 分运行 fetchmail。

在星号和范围之后允许步长值。它们的格式为 <range>/<step>。所以,

*/5 8-17/2 * * * cp /var/log/* /log/backup

将在每天上午 8 点、上午 10 点、中午、下午 2 点和下午 4 点每五分钟运行一次 cp 命令(以防您开始认为您只能运行 fetchmail)。

最后,名称可以用于月份(jan-dec,不区分大小写)和星期几(sun-sat,不区分大小写)。 Red Hat 手册页声称您不能在范围中使用名称,但我自己试了一下,它似乎可以正常工作。

运行时环境——高级 crontab 格式

这是最让 cron 用户感到困惑的领域。他们指定每天从交互式 shell 运行的命令,然后将它们放入 crontab 中,但它们不起作用或行为与他们预期的不同。

例如,如果您编写了一个名为“fardels”的程序并将其放在 &HOME/bin 中,然后将 $HOME/bin 添加到您的 PATH 中,cron 可能会向您发送如下邮件

/bin/sh: fardels: command not found

cron 使用的 PATH 不一定与您的交互式 shell 使用的 PATH 相同。

有必要了解 cron 任务运行的环境不是他们每天操作的环境。

首先,他们的普通环境变量都没有像在登录 shell 中那样初始化。以下环境变量由 cron 守护进程设置

SHELL=/bin/sh
LOGNAME  set from /etc/passwd entry for the crontab's UID.
HOME  set from /etc/passwd entry for the crontab's UID.

我们一直在隐瞒您。您的 crontab 文件中允许另一种类型的条目。 iname=value 形式的行允许设置环境变量,这些环境变量将在任务从 crontab 中运行时设置。您可以设置除 LOGNAME 之外的任何环境变量。

需要注意的一个重要变量是 MAILTO。如果未定义 MAILTO,则任务的输出将通过邮件发送给 crontab 的所有者。如果定义了 MAILTO 但为空,则会禁止邮件输出。否则,您可以指定一个电子邮件地址,用于发送 cron 任务的输出。

最后,任务条目的命令部分中的任何百分号都被视为换行符。第一个百分号之后的任何数据都作为标准输入传递给任务,因此您可以使用它来按计划调用交互式程序。

权限

拥有和使用 crontab 的能力以与 at 子系统非常相似的方式进行控制。 /etc/cron.allow 和 /etc/cron.deny 这两个文件确定谁可以使用 crontab。与 at 的情况一样,首先检查 cron.allow。如果存在,则只有其中列出的用户可以拥有 cron 任务。如果不存在,则读取 cron.deny 文件。除其中列出的用户外,所有用户都可以拥有 cron 任务。

如果这两个文件都不存在(这与“at”非常不同),则所有用户都可以拥有 crontab。

cron 守护进程

这里几乎没有什么可记录的。 cron 守护进程(称为 cron 或 crond)不接受任何参数,也不会以特殊方式响应任何信号。它在启动时检查 /var/spool/cron 目录中是否有与 /etc/passwd 中的用户名匹配的文件。这些文件被读入内存。 cron 每分钟唤醒一次,并遍历其任务列表,执行计划在该分钟内的任何任务。

每分钟,它还会检查 /var/spool/cron 目录自上次读取以来是否已更改,并重新读取任何修改,从而自动更新计划。

系统 crontab

到目前为止,我已经引导您进行了一场有趣的舞蹈。我让您认为只有用户拥有 crontab,并且所有计划任务都以 crontab 的所有者用户身份运行。这几乎是真的。 Cron 还提供了一种在“系统”级别指定 crontab 的方法。除了检查 /var/spool/cron 之外,cron 守护进程还会查找 /etc/crontab 和 /etc/cron.d 目录。

/etc/crontab 文件和 /etc/cron.d 中的文件是“系统 crontab”。这些文件的格式与到目前为止讨论的格式略有不同。

关键区别是在“星期几”字段和命令字段之间插入了一个字段。此字段是“以用户身份运行”字段。因此

02 4 * * * root run-parts /etc/cron.daily

将在每天凌晨 4 点过 2 分以 root 身份运行“run-parts /etc/cron.daily”。

最终说明

就是这样。虽然 Linux 没有附带成熟且完整的批处理过程管理工具,但 at 和 cron 的组合仍然允许相当大的灵活性和强大功能。

请记住,我们已经介绍了这些工具的 Linux 版本,它们随目前大多数发行版一起提供。虽然市场上几乎每个 UNIX 系统都具有这些工具,但有些方面有所不同。

预计 at 队列会有所不同。并非所有 cron 都支持名称或范围。大多数不支持范围列表或增量功能。我熟悉的任何其他 cron 都不支持在 crontab 中设置环境变量。我不认为任何其他 at 都支持将“teatime”作为时间规范。

这归结为一条基本建议。始终检查本地文档。如有疑问,请进行实验。

资源

Linux Job Scheduling
电子邮件:mschwarz@sherbtel.net

Michael Schwarz (mschwarz@sherbtel.net) 是明尼苏达州明尼阿波利斯市 Interim Technology Consulting 的顾问。他拥有 15 年的 UNIX 软件编写经验,并领导开源 SASi 项目。自 1994 年下载 TAMU 版本以来,他一直在使用 Linux,并将 SASi 项目维护在 http://alienmystery.planetmercury.net/

加载 Disqus 评论