cron 用于周期性执行定时任务,它的 daemon 叫做 crond,常驻内存。crontab 是用于编辑管理 cron 的指令。同样的,anacron 是指令,anacrontab 是其配置文件。

作为系统服务,cron 可以由 systemctl 管理,能够以分钟精度执行任务。而 anacron 仅是一个指令,需要 cron 调用才能运行。cron 认为主机会 24 小时不间断运行。 anacron 考虑到宕机等实际问题,认为主机不可能不出意外一直连续运行下去,因此会定时执行因意外导致无法被 cron 执行的任务。anacron 可以算是 cron 的补充和完善。

查看 cron 帮助文档,知道 cron 会检查的文件和目录有:

  • /etc/crontab
  • /etc/cron.d/
  • /var/spool/cron/

/var/spool/cron/

一般我们使用命令:『crontab -e』来设定定时任务,实际上此命令会在 /var/spool/cron/ 目录下创建一个以当前用户为文件名的文件,内容即是 crontab -e 的内容。例如使用 root 用户设置如下:

1
0 3 * * * echo hello world > /tmp/hello.txt

我们会在 /var/spool/cron/root 文件里看到同样的内容。虽然我们可以直接编辑此文件来设定定时任务,但是还是建议使用 crontab -e 命令,因为 crontab 可以自动判断语法问题。例如:我错误的写成 0 3 * * 少写一个 * 符号,保存退出后会提示错误信息:bad day-of-week。

1
2
0 3 * * echo hello world > /tmp/hello.txt
crontab: installing new crontab "/tmp/crontab.gCbZsp":1: bad day-of-week errors in crontab file, can\'t install.Do you want to retry the same edit?

/etc/crontab

cron 除了会读取 crontab -e 命令设置的 /var/spool/cron 目录下的文件外,它自己的配置文件:/etc/crontab 也是设定定时任务的地方。跟前面不同的是,配置文件处需要标明执行命令的用户身份。

1
01  *  *  *  *   root      run-parts /etc/cron.hourly

上面说的是每小时 01 分以 root 用户身份执行 run-parts /etc/cron.hourly。通过 which 命令查找知道,run-parts 是一个系统定义的用于执行某个目录下所有脚本的 bash 脚本而已。因此当我们写了一个想要定时执行的脚本,可以根据定时执行的频率将脚本丢到对应的 cron 目录去,目录有 cron.hourly、cron.daily、cron.weekly 和 cron.monthly。

但你查看 /etc/crontab 文件,你是看不到上面的定时任务的。因为现在默认不在此处设置定时任务,原因是 anacron 同样会有如上的定时任务命令,避免冲突就给 anacron 设置了。帮助文档说法:

By default  these jobs are now run through anacron which reads /etc/anacrontab configuration file.

也就是说 /etc/crontab 文件就只剩下环境变量的设定,定时任务 “全权” 交由 anacron 管理。

/etc/cron.d/

一直藏着不见的 anacron 就是在这里被 cron 调用的。cron 除了会读取上面两个文件外,还会读取这个目录下的所有文件。其中有个名为 0hourly 的文件内容:

1
2
3
4
5
# Run the hourly jobs
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
01 * * * * root run-parts /etc/cron.hourly

可以看到定义了每小时使用 root 身份使用 run-parts 命令运行 /etc/cron.hourly 目录下的所有脚本。然后前往 /etc/cron.hourly 目录,发现有且只有一个 0anacron 文件,内容最后一行分明写着:

1
/usr/sbin/anacron -s

cron 就是这样调用 anacron 的,而anacron 的配置文件 /etc/anacrontab 就可以明确找到对  cron.daily、cron.weekly 和 cron.monthly  四个目录的定时任务命令了。没有 cron.hourly 是因为 anacron 的精度只到 “天” 级别。另外 /etc/cron.d/ 通过放置 0hourly 文件来间接调用 cron.hourly,也没丢掉 “小时” 级别精度下的控制。

查阅 anacrontab 的帮助文档,下面的原文大意是:如果想要禁用 anacron,将 0anacron 文件移动到 /etc/cron.hourly/jobs.deny 目录下。

In case you want to disable Anacron, add the 0anacron cron job (which is a part of crontab(1)) into the /etc/cron.hourly/jobs.deny directory.

这样也反向证明了 cron 的确是通过读取 /etc/cron.d/0hourly 调用 /etc/cron.hourly/0anacron 脚本来运行 anacron 的。

anacron

crontab 配置文件内的定时任务交给 anacron 管理后,anacron 会到点儿执行任务,并在执行完成后将当前时间精确到 “天” 记录到 /var/spool/anacron/ 对应的文件里,标记这次执行成功的时间。等到下次执行任务时,首先对比上次执行成功的时间,再决定是否执行。

例如:在 3 月 10 号因为意外导致主机宕机,原定每天凌晨 1:00 的任务没有执行,那么 anacron 记录的任务成功执行的时间是在 3 月 9 日。因为 anacron 是通过 cron 的 cron.hourly 以 ”小时” 为精度被调用的,假设主机在凌晨 2:00 恢复运行,那么 cron 会在 2:01 调用 anacron,anacron 读取记录知道上次执行任务是在 3 月 9 日,需要再次执行任务。所以 anacron 可以在任何时间执行未执行的任务。

anacron 在执行任务过程中,如果有信息输出,默认会邮件给执行 anacron 的用户;如果指定了变量 MAILTO,那么会优先发送给他。日志信息会记录在 cron 里,级别为 notice。

1
2
3
4
5
6
7
8
9
10
11
12
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22

#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly

anacrontab 的定时任务格式跟 crontab 很像。第一个数字表示执行任务的间隔,单位天,第一个数字表示延迟时间,单位分钟。设置延迟是为了避免所有任务都在同一时间执行造成拥堵,因此在此基础上增加了变量 RANDOM_DELAY,最大随机延迟。