例行性工作(crontab)at, cron

回到顶部

前面介绍了在linux后台有许多默默执行的系统服务,这些系统服务称为系统守护进程, 下面我们就介绍两个比较重要的系统守护进程atd和crond。 我们在linux命令行下输入命令然后按下enter键,命令会立即执行。 但是有时候我们并不希望命令立即执行,而是希望在未来某个时刻执行。 atd服务就是可以在未来指定的某个时刻执行指定命令任务的系统守护进程,可以通过at命令设置atd服务要执行的命令和时间。
linux下还有一种任务,会定期在某个时刻执行,但是执行完之后,隔段时间后又会被执行, 也就是会周期性循环执行的任务。比如每周五下午都要定期备份系统中比较重要的文件。 crond服务就是可以周期性循环执行指定任务的系统守护进程,通过crontab命令设置crond服务周期性执行的命令和时间。 可循环的时间周期为分钟、小时、每周、每月或每年等。除了用crontab命令设置crond系统服务外,也可以通过编辑/etc/crontab文件配置crond服务。

Linux系统上常见的周期性例行工作

回到顶部

linux系统要维持正常运作的话, 需要在后台定期执行一些任务。 比如自动在线更新(on-line update)、自动更新updatedb文件名locate数据库、自动分析日志文件等等。
Linux系统中常见的例行性任务有:
日志文件的更替(log rotate):
Linux会主动的将系统所发生的各种信息都记录下来,这就是日志文件。 由于系统会一直记录日志信息,所以日志文件将会越来越大!我们知道大型文件不但占容量还会造成读写效能的困扰, 因此适时的将日志文件数据挪一挪,让旧的数据与新的数据分别存放,则可以有效的记录日志信息。 这就是log rotate的任务! 也是系统必要的例行任务;
日志文件分析logwatch任务:
系统可能会发生一些软硬件错误,或者偶尔会发生系统安全问题,绝大部分的错误信息都会被记录到日志文件中。 因此系统管理员的重要任务之一就是分析日志文件。日志文件数据往往比较庞大而且复杂,通过vim查看日志文件手动分析日志文件,效率会非常低下。 可以通过logwatch工具分析系统日志信息,所以系统需要定期执行logwatch程序来主动分析日志信息。 这也是为什么root总是会收到标题为logwatch的邮件!
建立locate数据库:
locate指令通过建立的系统文件名数据库来进行文件名的查询。文件名数据库文件存放在/var/lib/mlocate/目录中。 通过updatedb命令可以手动更新数据库,但是我们总不能一直这么手动更新把吧,所以就需要系统帮助我们自动更新数据库文件了
建立man page查询数据库:
与locate数据库类似,可提供快速帮助查询功能的man page也需要数据库mandb, 使用mandb工具建立man page数据库。 我们也需要定期执行mandb来更新man page数据库。
建立RPM软件日志文件的建立:
RPM是一种软件管理的机制。系统可能会经常变更软件, 包括软件的安装与更新等,都会造成软件文件名的差异。为了方便未来追踪,系统也帮我们将文件名作个排序的记录呢! 这时候系统也需要定期更新RPM数据库。
清除暂存文件:
某些软件在运行中会产生一些暂存文件,当这个软件退出时,产生的暂存文件可能并不一定会主动的被移除。 有些暂存文件可能有时效性,超过一段时间后就没有效用了,此时移除这些暂存盘就是一件重要的工作! 否则磁盘容量会被耗光。系统可以通过定期执行tmpwatch来删除这些暂存文件!
分析网络服务:
web服务软件,如nginx或apache,会自己产生一些日志文件,linux需要定期分析这些软件的日志文件。 同时某些凭证与认证的网络信息是否过期的问题,我们的 Linux 系统也会很亲和的帮你进行自动检查!
其实linux系统会进行的例行性工作与系统中安装的软件多少有关, 如果安装过多的软件,某些服务功能的软件都会附上分析工具, 那么你的系统就会多出一些例行性工作! 很多主机还多加了很多系统管理员自己编写的分析工具,以及其他第三方软件的分析工具, 这样Linux的在后台需要定期执行的工作量其实是非常大的!
首先,我们先来谈谈单一工作排程的运作,那就是 at 这个指令的运作!

atd 的启动与 at 运作的方式

回到顶部

上面提到,atd服务负责在未来某个时刻执行某个任务,at命令可以对atd服务进行配置。 所以在使用at命令的时候,首先需要确保系统中已经安装并且启动了atd服务。 大部分linux发行版都默认安装和启动了该服务。如果没有安装, 可以通过apt/yum install at安装at以及atd服务。 通过systemctl查看该服务的状态:

[root@initroot ~]# systemctl restart atd  # 重启atd服务
[root@initroot ~]# systemctl enable atd   # 让atd服务开机自动启动
[root@initroot ~]# systemctl status atd   # 查看atd服务的状态
systemctl status atd
● atd.service - Deferred execution scheduler
   Loaded: loaded (/lib/systemd/system/atd.service; enabled; vendor preset: enabled)  #enabled表示开机自动启动
   Active: active (running) since Thu 2020-01-30 22:19:00 CST; 9s ago    #atd服务正在运作中
     Docs: man:atd(8)
 Main PID: 8732 (atd)
    Tasks: 1 (limit: 4915)
   CGroup: /system.slice/atd.service
           └─8732 /usr/sbin/atd -f

Jan 30 22:19:00 peter-VirtualBox systemd[1]: Started Deferred execution scheduler.
关于systemctl命令,我们在前面已经详细介绍过了,这里就不再赘述了。

at 的运作方式

回到顶部

我们使用at命令来设置未来某个时刻要运行的命令,at将工作以文本文件的方式写入/var/spool/at/目录内, atd服务进程便会自动读取该目录内的工作设置。
出于安全的考虑,我们并不希望所有用户都可以使用at命令,如果系统被黑客侵入, 很有可能会利用atd服务,在系统中装入特定的黑客程序,定期搜索系统的重要信息。 所以最好是指定几个信任的账号可以使用at命令。
利用/etc/at.allow与/etc/at.deny这两个文件来进行at命令的使用限制! 在执行at命令的时候,at会首先读取这两个文件判断用户是否有权限执行:
1. 首先读取/etc/at.allow文件,记录在该文件中的用户才能使用at,不在该文件中的用户不能使用at(即使没有写在 at.deny 当中);
2. 如果/etc/at.allow文件,就读取/etc/at.deny文件,记录在该文件中的用户不能使用at,不在该文件中的用户可以使用at;
3. 如果两个文件都不存在,那么只有root可以使用at指令。
透过这个说明,我们知道/etc/at.allow是管理较为严格的方式,而/etc/at.deny则较为松散。 因为账号没有在该文件中,就能够执行at了。
在一般的 distributions 当中,由于假设系统上的所有用户都是可信任的, 因此系统通常会保留一个空的/etc/at.deny文件,意思是允许所有人使用at指令的意思。 如果不希望某个用户使用at的话,将那个使用者的账号写入/etc/at.deny即可! 一个账号写一行。

实际运作单一工作排程

回到顶部

at命令常用格式如下所示:

[root@initroot ~]# at [-mldv] TIME
[root@initroot ~]# at -c 工作号码
选项与参数:
-m :当 at 的工作完成后,即使没有输出讯息,亦以 email 通知使用者该工作已完成。
-l :at -l 相当于 atq,列出目前系统上面的所有该用户的 at 排程;
-d :at -d 相当于 atrm ,可以取消一个在 at 排程中的工作;
-v :可以使用较明显的时间格式栏出at排程中的任务栏表;
-c :可以列出后面接的该项工作的实际指令内容。
TIME:时间格式,定义什么时候要进行at这项工作的时间,格式有:
HH:MM ex> 04:00
在今日的 HH:MM 时刻进行,若该时刻已超过,则明天的 HH:MM 进行此工作。
HH:MM YYYY-MM-DD ex> 04:00 2015-07-30
强制规定在某年某月的某一天的特殊时刻进行该工作!
HH:MM[am|pm] [Month] [Date] ex> 04pm July 30
也是一样,强制在某年某月某日的某时刻进行!
HH:MM[am|pm] + number [minutes|hours|days|weeks]
ex> now + 5 minutes
ex> 04pm + 3 days
就是说,在某个时间点再加几个时间后才进行。
at命令最重要的地方在于时间的设定。通常使用now + number的方式来定义现在过多少时间再进行工作,但有时也需要定义特定的时间点来进行!
除了at命令,还有atq和atrm命令,atq查询目前主机上面有多少的at工作排程,相当于at -l
如果下达了at之后,发现刚才输入的指令有错误,该如何是好?就利用atq将工作移除!
[root@initroot ~]# atq
[root@initroot ~]# atrm (jobnumber)
过五分钟后,将/root/.bashrc寄给root:
[root@initroot ~]# at now + 5 minutes   #记得单位要加 s 喔!
at> /bin/mail -s "testing at job" root < /root/.bashrc
at> <EOT>    #这里输入 [ctrl] + d 就会出现 <EOF> 的字样!代表结束!
job 2 at Thu Jul 30 19:35:00 2015         #第2个at工作将在2015/07/30 的 19:35进行!
            
执行at命令会进入所谓的at shell环境,让你下达多重指令等待运作!
将第2项工作内容列出来查阅:
[root@initroot ~]# at -c 2
#就是透过 bash shell 的啦!
#!/bin/sh
# atrun uid=0 gid=0
# mail root 0
umask 22
....(中间省略许多的环境变量项目)....
cd /etc/cron\.d || {
echo 'Execution directory inaccessible' >&2
exit 1
}
${SHELL:-/bin/sh} << 'marcinDELIMITER410efc26'
/bin/mail -s "testing at job" root < /root/.bashrc  # 这一行最重要!
marcinDELIMITER410efc26
可以看到指令执行的目录/root,还有多个环境变量与实际的指令内容啦!
假设机房预计于2015/08/05停电,需要在2015/08/04 23:00关机:
[root@initroot ~]# at 23:00 2015-08-04
at> /bin/sync
at> /bin/sync
at> /sbin/shutdown -h now
at> <EOT>
job 3 at Tue Aug 4 23:00:00 2015
可以在at shell环境下输入多个指令!
当使用at命令时会进入一个at shell环境来让用户下达工作指令,建议最好使用绝对路径来下达指令! 由于指令的下达与PATH变量有关, 同时与当时的工作目录也有关连 (如果有牵涉到文件的话),因此使用绝对路径来下达指令, 会是比较一劳永逸的方法。 举例来说,你在 /tmp 下达at now然后输入mail -s "test" root < .bashrc, 那个.bashrc文件会是/tmp/.bashrc!因为at在运作时,会跑到当时下达 at 指令的那个工作目录的缘故啊!
有时候我们可能希望在未来某个时刻,屏幕上可以给我们一些提示。例如我希望明天下午两点提醒我给美国总统打个电话。 那么就可以在at shell环境里输入echo "call American president!"。 但是等到时间了,却发现屏幕上没有任何提示信息。这么重要的事情怎么能错过呢。 这是由于atd服务是系统守护进程,我们在讨论守护进程的时候提到过,守护进程属于linux后台进程,没有控制终端。 而所有由atd服务进程启动的命令进程都是atd进程的子进程,自然也就不能有控制终端了,所以如果有信息输出的话,是不会在屏幕上显示的。 所有的standard output/standard error output都会传送到执行者的mailbox中去! 可以透过终端机的装置来处理!可以通过强制输出重定向将输出重定向到终端设备,假如你登录终端为tty1,则可以使用echo "Hello" > /dev/tty1来取代。
。 如果在at shell内的指令并没有任何输出,at默认不会发email给执行者的。 如果你想要让at无论如何都发一封email告知你是否执行了指令,可以使用at -m 时间格式来下达指令! at就会传送一个讯息给执行者,而不论该指令执行有无讯息输出了!
at有另外一个很棒的优点,那就是后台执行的功能了!与nohup命令类似!
由于at工作排程的使用上,系统会将该项at工作独立出你的bash环境中, 直接交给系统的atd程序来接管, 因此,当你下达了at工作之后就可以立刻脱机了, 剩下的工作就完全交给Linux管理即可! 所以啰,如果有长时间的网络工作时,使用 at 可以让你免除网络断线后的困扰喔!
查询目前主机上面有多少的at工作排程?
[root@initroot ~]# atq
3
Tue Aug
4 23:00:00 2015 a root
上面说的是:在 2015/08/04 的 23:00 有一项工作,该项工作指令下达者为root而且,该项工作的工作号码 (jobnumber) 为 3 号喔!
将上述的第 3 个工作移除!
[root@initroot ~]# atrm 3
[root@initroot ~]# atq
没有任何信息,表示该工作被移除了! 如此一来,你可以利用 atq 来查询,利用 atrm 来删除错误的指令,利用 at 来直接下达单一工作排程!
如果在一个非常忙碌的系统下运作at,可能会导致cpu负载过高而导致系统运行效率低下。 可以使用batch命令指定工作在系统较闲的时候才进行。

batch:系统空闲时才进行后台任务

回到顶部

batch实际上是利用at来进行指令的下达,只是加入一些控制参数而已。 batch会在CPU的工作负载小于0.8的时候,才进行你所下达的工作任务啦! cpu的工作负载是指CPU在单一时间点所负责的工作数量。注意不是CPU的使用率! 举例来说,如果我有一只程序他需要一直使用 CPU 的运算功能,那么此时 CPU 的使用率可能到达100% , 但是 CPU 的工作负载则是趋近于1,因为 CPU 仅负责一个工作嘛! 如果同时执行这样的程序两支呢? CPU 的使用率还是 100% ,但是工作负载则变成 2 了!
所以也就是说,当 CPU 的工作负载越大,代表 CPU 必须要在不同的工作之间进行频繁的工作切换。 这样的 CPU 运作情况我们在第零章有谈过,忘记的话请回去瞧瞧!因为一直切换工作,所以会导致 系统忙碌啊! 系统如果很忙碌,还要额外进行 at ,不太合理!所以才有 batch 指令的产生!
在 CentOS 7 底下的 batch 已经不再支持时间参数了,因此 batch 可以拿来作为判断是否要立刻执 行背景程序的依据! 我们底下来实验一下 batch 好了!为了产生 CPU 较高的工作负载,因此我们 用了 12 章里面计算 pi 的脚本,连续执行 4 次这只程序, 来仿真高负载,然后来玩一玩 batch 的工作现象: 范例一:请执行 pi 的计算,然后在系统闲置时,执行 updatdb 的任务

[root@initroot ~]# echo "scale=100000; 4*a(1)" | bc -lq &
[root@initroot ~]# echo "scale=100000; 4*a(1)" | bc -lq &
[root@initroot ~]# echo "scale=100000; 4*a(1)" | bc -lq &
[root@initroot ~]# echo "scale=100000; 4*a(1)" | bc -lq &
# 然后等待个大约数十秒的时间,之后再来确认一下工作负载的情况!
[root@initroot ~]# uptime
19:56:45 up 2 days, 19:54, 2 users, load average: 3.93, 2.23, 0.96
            
[root@initroot ~]# batch
at> /usr/bin/updatedb
at> <EOT>
job 4 at Thu Jul 30 19:57:00 2015
            
[root@initroot ~]# date;atq
Thu Jul 30 19:57:47 CST 2015
4 Thu Jul 30 19:57:00 2015 b root
# 可以看得到,明明时间已经超过了,却没有实际执行 at 的任务!
[root@initroot ~]# jobs
[1] Running echo "scale=100000; 4*a(1)" | bc -lq &
[2] Running echo "scale=100000; 4*a(1)" | bc -lq &
[3]- Running echo "scale=100000; 4*a(1)" | bc -lq &
[4]+ Running echo "scale=100000; 4*a(1)" | bc -lq &
[root@initroot ~]# kill -9 %1 %2 %3 %4
          
# 这时先用 jobs 找出背景工作,再使用 kill 删除掉四个背景工作后,慢慢等待工作负载的下降
[root@initroot ~]# uptime; atq
20:01:33 up 2 days, 19:59, 2 users, load average: 0.89, 2.29, 1.40
4 Thu Jul 30 19:57:00 2015 b root
[root@initroot ~]# uptime; atq
20:02:52 up 2 days, 20:01, 2 users, load average: 0.23, 1.75, 1.28
# 在 19:59 时,由于 loading 还是高于 0.8,因此 atq 可以看得到 at job 还是持续再等待当中喔! # 但是到了 20:01 时, loading 降低到 0.8 以下了,所以 atq 就执行完毕啰!
使用 uptime 可以观察到 1, 5, 15 分钟的平均工作负载量,因为是平均值,所以当我们如上表删 除掉四个工作后,工作负载不会立即降低, 需要一小段时间让这个 1 分钟平均值慢慢回复到接近 0 啊!当小于 0.8 之后的整分钟时间时,atd 就会将 batch 的工作执行掉了!
什么是整分钟时间呢?不论是 at 还是底下要介绍的 crontab,他们最小的时间单位是分钟, 所以,基本上,他们的工作是每分钟检查一次来处理的! 就是整分 (秒为 0 的时候),这样了 解乎?同时,你会发现其实 batch 也是使用 atq/atrm 来管理的!

循环执行的例行性工作排程

回到顶部

相对于 at 是仅执行一次的工作,循环执行的例行性工作例程则是由 cron (crond) 这个系统服务来控 制的。刚刚谈过 Linux 系统上面原本就有非常多的例行性工作,因此这个系统服务是默认启动的。 另外, 由于使用者自己也可以进行例行性工作排程,所以啰, Linux 也提供使用者控制例行性工作排程的指令 (crontab)。 底下我们分别来聊一聊啰!

使用者的设定

回到顶部

使用者想要建立循环型工作排程时,使用的是 crontab 这个指令啦~不过,为了安全性的问题, 与 at 同样的,我们可以限制使用 crontab 的使用者账号喔!使用的限制数据有:
/etc/cron.allow:
将可以使用 crontab 的账号写入其中,若不在这个文件内的使用者则不可使用 crontab;
/etc/cron.deny:
将不可以使用 crontab 的账号写入其中,若未记录到这个文件当中的使用者,就可以使用 crontab 。 与 at 很像吧!同样的,以优先级来说, /etc/cron.allow 比 /etc/cron.deny 要优先, 而判断上面,这 两个文件只选择一个来限制而已,因此,建议你只要保留一个即可, 免得影响自己在设定上面的判断! 一般来说,系统默认是保留 /etc/cron.deny , 你可以将不想让他执行 crontab 的那个使用者写 入 /etc/cron.deny 当中,一个账号一行!
当用户使用 crontab 这个指令来建立工作排程之后,该项工作就会被纪录到 /var/spool/cron/ 里面去 了,而且是以账号来作为判别的喔!举例来说, peter 使用 crontab 后, 他的工作会被纪录到/var/spool/cron/peter 里头去! 但请注意,不要使用 vi 直接编辑该文件, 因为可能由于输入语法错误,会导致无法执行 cron 喔! 另外, cron 执行的每一项工作都会被纪录到 /var/log/cron 这个登录档中,所以啰,如果你的 Linux 不知道有否被植入木马时, 也可以搜寻一下 /var/log/cron 这个登录档呢!
好了,那么我们就来聊一聊 crontab 的语法吧!

              [root@initroot ~]# crontab [-u username] [-l|-e|-r]
            
选项与参数:
-u :只有 root 才能进行这个任务,亦即帮其他使用者建立/移除 crontab 工作排程;
-e :编辑 crontab 的工作内容
-l :查阅 crontab 的工作内容
-r :移除所有的 crontab 的工作内容,若仅要移除一项,请用 -e 去编辑。
让用户peter每天12:00给自己发送邮件:
              [peter@study ~]$ crontab -e
              # 此时会进入 vi 的编辑画面让您编辑工作!注意到,每项工作都是一行。
              0 12 * * * mail -s "at 12:00" peter < /home/peter/.bashrc
              #分 时 日 月 周 |#============指令串========================>|
            
只要没有记录在/etc/cron.deny文件中的用户都可以直接下达crontab -e命令编辑例行性任务! 此时crontab会调用vi进入编辑界面,编辑完毕后输入:wq保存后即可离开vi编辑界面。 每项工作占用一行,共有六个字段,这六个字段的意义如下所示,括号内为该字段的取值范围:
分钟(0-59) 小时(0-23) 日期(1-31) 月份(1-12) 周(0-7) 指令
前面五个字段为时间字段,最后一个字段为命令任务字段。 需要注意的是周字段,周的数字为0或7时,都代表星期天! 时间字段可以有有一些辅助的字符:
星号* 表示任意时刻。例如上面例子中日、月、周都是* , 表示不论何月、何日的礼拜几的12:00都执行后面的指令!也就是每天的12:00都会执行。
逗号, 如果有多个时间,多个时间用逗号分隔。例如我们希望上面的工作时间是每天的3:00和6:00,那么任务行就是如下所示:
0 3,6 * * * command
时间参数还是五个字段,第二个字段为3,6 ,表示3:00和6:00都会执行后面的command!
减号- 表示一段时间范围,例如8点到12点之间的每小时的20分都进行一项工作:
20 8-12 * * * command
第二个字段变成8-12!代表8:20, 9:20, 10:20, 11:20, 12:20都会执行后面的command!
斜线/n n表示数字,每隔n时间单位间隔,例如每五分钟进行一次,则:
*/5 * * * * command
用*与/5来搭配,也可以写成0-59/5。
我们就来搭配几个例子练习看看吧!底下的案例请实际用 peter 这个身份作看看喔!后续的动作才能够搭配起来!
后面的练习我们假设当前登录用户为peter。 假若peter女朋友cristina生日是5月2日,你想要在5月1日的23:59发一封信给他,这封信的内容已经写在/home/peter/lover.txt内:
              [peter@study ~]$ crontab -e
              59 23 1 5 * mail cristina < /home/peter/lover.txt
            
这样每年 kiki 都会收到peter的这封信喔!
每五分钟执行一次/home/peter/test.sh:
              [peter@study ~]$ crontab -e
              */5 * * * * /home/peter/test.sh
            
在/var/spool/cron目录下,每个用户都有一个对应的crontab文件存在 建议指令下达时,最好使用绝对路径,这样比较不会找不到执行档喔!
你每星期六都与朋友有约,那么想要每个星期五下午 4:30 告诉你朋友星期六的约会不要忘记:
              [peter@study ~]$ crontab -e
              30 16 * * 5 mail friend@his.server.name < /home/peter/friend.txt
            
查询使用者目前的 crontab 内容:
              [peter@study ~]$ crontab -l
              0 12 * * * mail -s "at 12:00" peter < /home/peter/.bashrc
              59 23 1 5 * mail cristina < /home/peter/lover.txt
              */5 * * * * /home/peter/test.sh
              30 16 * * 5 mail friend@his.server.name < /home/peter/friend.txt
            
如果要删除某项工作,只需要用crontab -e进入编辑界面,删除相应的行即可。 如果想要移除全部的工作,可以使用crontab -r:
              [peter@study ~]$ crontab -r
              [peter@study ~]$ crontab -l
              no crontab for peter
            
这样crontab整个内容都不见了!如果只是要删除某个 crontab 的工作项目,那么请使用 crontab -e 来重新编辑即可! 如果使用 -r 的参数,是会将所有的 crontab 数据内容都删掉的!

系统的配置文件: /etc/crontab, /etc/cron.d/*

回到顶部

上面的crontab命令其实就是在/var/spool/cron目录下创建用户对应的crontab文件, cron服务进程每隔一分钟就会读取该目录下的文件。通过crontab创建的任务都属于执行crontab命令的用户。 除了/var/spool/cron目录,cron服务进程每分钟还会读取/etc/crontab文件, /etc/crontab文件中记录的是系统需要定期执行的任务。 所以如果有一些系统性任务,我们就可以写入/etc/crontab文件中。 crond服务进程就会每分钟读取一次/etc/crontab文件,并根据该文件的设置执行相应的任务。 由于/etc/crontab文件是读到内存当中的,所以在修改完/etc/crontab之后,可能并不会马上执行, 这个时候需要重新启动crond服务进程:systemctl restart crond。
我们来看看/etc/crontab文件的内容:

              [root@initroot ~]# cat /etc/crontab
              SHELL=/bin/bash                       #使用哪种 shell 接口
              PATH=/sbin:/bin:/usr/sbin:/usr/bin    #执行文件搜寻路径
              MAILTO=root                           #若有额外 STDOUT,以 email 将数据送给谁
              # Example of job definition:
              # .---------------- minute (0 - 59)
              # | .------------- hour (0 - 23)
              # | | .---------- day of month (1 - 31)
              # | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
              # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
              # | | | | |
              # * * * * * user-name command to be executed

              # m h dom mon dow user	command
              17 *	* * *	root    cd / && run-parts --report /etc/cron.hourly
              25 6	* * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
              47 6	* * 7	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
              52 6	1 * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
              #

            
文件中的设置与将刚刚我们下达 crontab -e的内容几乎完全一模一样!区别是每一行任务多了一个user字段:
分 时 日 月 周 用户 指令
用户表示执行后面那串指令的用户! 这与使用者的 crontab -e 不相同。由于使用者自己的 crontab 并不需要指定身份, 但 /etc/crontab 里面当然要指定用户!系统默认的例行性工作是以root的身份来进行的。
另外文件的前面还有一些变量的设置,注意这里的变量并不是bash变量,而是cron服务进程自己内部使用变量: MAILTO=root:
这个项目是说,当/etc/crontab 这个文件中的例行性工作的指令发生错误时,或者是该工作的执行 结果有 STDOUT/STDERR 时,会将错误讯息或者是屏幕显示的讯息传给谁?默认当然是由系统直接寄发一封 mail 给 root 啦! 不过, 由于 root 并无法在客户端中以 POP3 之类的软件收信, 因此,通常都将这个 e-mail 改成自己的账号,好让我随时了解系统的状况!例如:
              ...
              MAILTO=peter@my.host.name
              ...
            
PATH=/sbin:/bin:/usr/sbin:/usr/bin 这里就是输入执行文件的搜寻路径!使用默认的路径设定就已经很足够了!
除了上面提到的/var/spool/cron/*和/etc/crontab文件外,crond服务进程还会读取/etc/cron.d/*文件 现在我们已经知道了/var/spool/cron/以及/etc/crontab文件的内容,现在就来看看/etc/cron.d目录下的文件:
              [root@initroot ~]# ls -l /etc/cron.d
              -rw-r--r--. 1 root root 128 Jul 30 2014 0hourly
              -rw-r--r--. 1 root root 108 Mar 6 10:12 raid-check
              -rw-------. 1 root root 235 Mar 6 13:45 sysstat
              -rw-r--r--. 1 root root 187 Jan 28 2014 unbound-anchor
            
除了/etc/crontab之外,crond的配置文件还不少!上面就有四个设定!
先瞧瞧0hourly配置文件的内容:
              [root@initroot ~]# cat /etc/cron.d/0hourly
              # Run the hourly jobs
              SHELL=/bin/bash
              PATH=/sbin:/bin:/usr/sbin:/usr/bin
              MAILTO=root
              01 * * * * root run-parts /etc/cron.hourly
            
内容跟 /etc/crontab 几乎一模一样!但实际上是有设定值喔!就是最后一行!
如果你想要自己开发新的软件,该软件要拥有自己的 crontab 定时指令时,就可以将分、时、日、 月、周、身份、指令的配置文件放置到 /etc/cron.d/ 目录下! 在此目录下的文件是crontab 的配置文件脚本。
以来说,现在有在开发一些虚拟化教室的软件,该软件需要定时清除一些垃圾防火墙规则, 那就是将要执行的时间与指令设计好,然后直接将设定写入到 /etc/cron.d/newfile 即可!未来 如果这个软件要升级, 直接将该文件覆盖成新文件即可!比起手动去分析 /etc/crontab 要单纯的多!
另外,请注意一下上面表格中提到的最后一行,每个整点的一分会执行run-parts /etc/cron.hourly。 什么是run-parts呢? 如果你有去分析一下这个执行档,会发现他就是 shell script, run-parts 脚本会在大约 5 分钟内随机选一个时间来执行/etc/cron.hourly目录内的所有执行文件! 因此,放在 /etc/cron.hourly/ 的文件,必须是能被直接执行的指令脚本, 而不是分、时、日、月、周的设定值喔!
也就是说,除了自己指定分、时、日、月、周加上指令路径的 crond 配置文件之外,你也可以直接将指令放置到(或链接到)/etc/cron.hourly/ 目录下, 则该指令就会被 crond 在每小时的 1 分开始后的5 分钟内,随机取一个时间点来执行啰!你无须手动去指定分、时、日、月、周就是了。
但是眼尖的朋友可能还会发现,除了可以直接将指令放到 /etc/cron.hourly/ 让系统每小时定时执行之外, 在 /etc/ 底下其实还有 /etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/, 那三个目录是代表每日、每周、每月各执行一次的意思。不过,跟 /etc/cron.hourly/ 不太一样的是, 那三个目录是由 anacron 所执行的,而 anacron 的执行方式则是放在/etc/cron.hourly/0anacron里面耶~跟前几代 anacron 是单独的 service 不太一样喔! 这部份留待下个小节再来讨论。
最后,让我们总结一下吧:
crond 服务读取配置文件的位置
一般来说,crond 预设有三个地方会有执行脚本配置文件,他们分别是:
/etc/crontab
/etc/cron.d/*
/var/spool/cron/*
这三个地方中,跟系统的运作比较有关系的两个配置文件是放在 /etc/crontab 文件内以及
/etc/cron.d/* 目录内的文件, 另外一个是跟用户自己的工作比较有关的配置文件,就是放在
/var/spool/cron/ 里面的文件群。 个人化的行为使用 crontab -e :如果你是依据个人需求来建立的例行工作排程, 建议直接使用 crontab -e来建立你的工作排程较佳! 这样也能保障你的指令行为不会被大家看到 (/etc/crontab 是大家都能读取的权 限喔!);
系统维护管理使用 vim /etc/crontab :如果你这个例行工作排程是系统的重要工作,为了让自己管理方便, 同时容易追踪,建议直接写入 /etc/crontab 较佳!
自己开发软件使用 vim /etc/cron.d/newfile :如果你是想要自己开发软件, 那当然最好就是使用全新的配置文件,并且放置于 /etc/cron.d/ 目录内即可。
固定每小时、每日、每周、每天执行的特别工作:如果与系统维护有关,还是建议放置到 /etc/crontab 中来 集中管理较好。 如果想要偷懒,或者是一定要再某个周期内进行的任务,也可以放置到上面谈到的几个目 录中,直接写入指令即可!

一些注意事项

回到顶部

有的时候,我们以系统的 cron 来进行例行性工作的建立时,要注意一些使用方面的特性。 举例来说,如果我们有四个工作都是五分钟要进行一次的,那么是否这四个动作全部都在同一个时间点进行? 如果同时进行,该四个动作又很耗系统资源,如此一来,每五分钟的某个时刻不是会让系统忙得要死? 呵呵!此时好好的分配一些运行时间就 OK 啦!所以,注意一下:
资源分配不均的问题
当大量使用 crontab 的时候,总是会有问题发生的,最严重的问题就是系统资源分配不均的问题, 以的系统为例,我有侦测主机流量的信息,包括:
流量
区域内其他 PC 的流量侦测
CPU 使用率RAM 使用率
在线人数实时侦测
如果每个流程都在同一个时间启动的话,那么在某个时段时,我的系统会变的相当的繁忙,所以,这 个时候就必须要分别设定啦!我可以这样做:

              [root@initroot ~]# vim /etc/crontab
              1,6,11,16,21,26,31,36,41,46,51,56 * * * * root CMD1
              2,7,12,17,22,27,32,37,42,47,52,57 * * * * root CMD2
              3,8,13,18,23,28,33,38,43,48,53,58 * * * * root CMD3
              4,9,14,19,24,29,34,39,44,49,54,59 * * * * root CMD4
            
看到了没?那个 , 分隔的时候,请注意,不要有空格符!(连续的意思)如此一来, 则可以将 每五分钟工作的流程分别在不同的时刻来工作!则可以让系统的执行较为顺畅呦!
取消不要的输出项目
另外一个困扰发生在 当有执行成果或者是执行的项目中有输出的数据时,该数据将会 mail 给MAILTO设定的账号, 好啦,那么当有一个排程一直出错(例如DNS的侦测系统当中,若DNS上层主机挂掉,那么你就会一直收到错误讯息!)怎么办? 呵呵!还记得第十章谈到的数据流重导向吧? 直接以数据流重导向将输出的结果输出到 /dev/null 这个垃圾桶当中就好了!
安全的检验
很多时候被植入木马都是以例行命令的方式植入的,所以可以藉由检查 /var/log/cron 的内容来视察 是否有非您设定的 cron 被执行了?这个时候就需要小心一点啰!
周与日月不可同时并存
另一个需要注意的地方在于:你可以分别以周或者是日月为单位作为循环,但你不可使用「几月几 号且为星期几」的模式工作。 这个意思是说,你不可以这样编写一个工作排程:
30 12 11 9 5 root echo "just test"
#这是错误的写法
本来你以为九月十一号且为星期五才会进行这项工作,无奈的是,系统可能会判定每个星期五作一次, 或每年的 9 月 11 号分别进行,如此一来与你当初的规划就不一样了~所以啰,得要注意这个地方!
根据某些人的说法,这个月日、周不可并存的问题已经在新版中被克服了~ 不过,并没有实际去验证他!目前也不打算验证他! 因为,周就是周,月日就月日,单一执行点就单一执行点,无须 使用 crontab 去设定固定的日期啊!您说是吧? 15.4 可唤醒停机期间的工作任务 想象一个环境,你的 Linux 服务器有一个工作是需要在每周的星期天凌晨 2 点进行,但是很不巧的, 星期六停电了~所以你得要星期一才能进公司去启动服务器。 那么请问,这个星期天的工作排程还 要不要进行?因为你开机的时候已经是星期一,所以星期天的工作当然不会被进行,对吧!
问题是,若是该工作非常重要 (例如例行备份), 所以其实妳还是希望在下个星期天之前的某天还是 进行一下比较好~那你该怎办?自己手动执行?如果你跟一样是个记忆力超差的家伙,那么肯定 记不起来某个重要工作要进行的啦! 这时候就得要靠 anacron 这个指令的功能了!这家伙可以 主动帮你进行时间到了但却没有执行的排程喔!

什么是 anacron

回到顶部

anacron 并不是用来取代 crontab 的,anacron 存在的目的就在于我们上头提到的,在处理非 24 小 时一直启动的 Linux 系统的 crontab 的执行! 以及因为某些原因导致的超过时间而没有被执行的排 程工作。
其实 anacron 也是每个小时被 crond 执行一次,然后 anacron 再去检测相关的排程任务有没有被执 行,如果有超过期限的工作在, 就执行该排程任务,执行完毕或无须执行任何排程时,anacron 就停止了。
由于 anacron 预设会以一天、七天、一个月为期去侦测系统未进行的 crontab 任务,因此对于某些 特殊的使用环境非常有帮助。 举例来说,如果你的 Linux 主机是放在公司给同仁使用的,因为周末 假日大家都不在所以也没有必要开启, 因此你的 Linux 是周末都会关机两天的。但是 crontab 大多 在每天的凌晨以及周日的早上进行各项任务, 偏偏你又关机了,此时系统很多 crontab 的任务就无 法进行。 anacron 刚好可以解决这个问题!
那么 anacron 又是怎么知道我们的系统啥时关机的呢?这就得要使用 anacron 读取的时间记录文件 (timestamps) 了! anacron 会去分析现在的时间与时间记录文件所记载的上次执行 anacron 的时间, 两者比较后若发现有差异, 那就是在某些时刻没有进行 crontab 啰!此时 anacron 就会开始执行未 进行的 crontab 任务了!

anacron 与 /etc/anacrontab

回到顶部

anacron 其实是一支程序并非一个服务!这支程序在 CentOS 当中已经进入 crontab 的排程喔!同时 anacron 会每个小时被主动执行一次喔! 咦!每个小时?所以 anacron 的配置文件应该放置在 /etc/cron.hourly 吗?嘿嘿!您真内行~赶紧来瞧一瞧:

              [root@initroot ~]# cat /etc/cron.hourly/0anacron
              #!/bin/sh
              # Check whether 0anacron was run today already
              if test -r /var/spool/anacron/cron.daily; then
              day=`cat /var/spool/anacron/cron.daily`fi
              if [ `date +%Y%m%d` = "$day" ]; then
              exit 0;
              fi
              # 上面的语法在检验前一次执行 anacron 时的时间戳!
              # Do not run jobs when on battery power
              if test -x /usr/bin/on_ac_power; then
              /usr/bin/on_ac_power >/dev/null 2>&1
              if test $? -eq 1; then
              exit 0
              fi
              fi
              /usr/sbin/anacron -s
            
# 所以其实也仅是执行 anacron -s 的指令!因此我们得来谈谈这支程序! 基本上, anacron 的语法如下:
              [root@initroot ~]# anacron [-sfn] [job]..
              [root@initroot ~]# anacron -u [job]..
            
选项与参数:
-s :开始一连续的执行各项工作 (job),会依据时间记录文件的数据判断是否进行;
-f :强制进行,而不去判断时间记录文件的时间戳;
-n :立刻进行未进行的任务,而不延迟 (delay) 等待时间;
-u :仅更新时间记录文件的时间戳,不进行任何工作。
job :由 /etc/anacrontab 定义的各项工作名称。
在我们的 CentOS 中,anacron 的进行其实是在每个小时都会被抓出来执行一次, 但是为了担心 anacron 误判时间参数,因此 /etc/cron.hourly/ 里面的 anacron 才会在文件名之前加个 0 (0anacron),让 anacron 最先进行!就是为了让时间戳先更新!以避免 anacron 误判 crontab 尚未进行任何工作的意思。
接下来我们看一下 anacron 的配置文件: /etc/anacrontab 的内容好了:
              [root@initroot ~]# cat /etc/anacrontab
              SHELL=/bin/sh
              PATH=/sbin:/bin:/usr/sbin:/usr/bin
              MAILTO=root
              RANDOM_DELAY=45 # 随机给予最大延迟时间,单位是分钟
              START_HOURS_RANGE=3-22 # 延迟多少个小时内应该要执行的任务时间
              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
            
天数 工作名称定义 实际要进行的指令串 延迟时间 # 天数单位为天;延迟时间单位为分钟;工作名称定义可自定义,指令串则通常与 crontab 的设定相同!
              [root@initroot ~]# more /var/spool/anacron/*
              ::::::::::::::
              /var/spool/anacron/cron.daily
              ::::::::::::::
              20150731
              ::::::::::::::
              /var/spool/anacron/cron.monthly
              ::::::::::::::
              20150703
              ::::::::::::::
              /var/spool/anacron/cron.weekly
              ::::::::::::::
              20150727
            
# 上面则是三个工作名称的时间记录文件以及记录的时间戳
我们拿 /etc/cron.daily/ 那一行的设定来说明好了。那四个字段的意义分别是:
天数:anacron 执行当下与时间戳 (/var/spool/anacron/ 内的时间纪录文件) 相差的天数,若超过此天数,就 准备开始执行,若没有超过此天数,则不予执行后续的指令。
延迟时间:若确定超过天数导致要执行排程工作了,那么请延迟执行的时间,因为担心立即启动会有其他 资源冲突的问题吧!
工作名称定义:这个没啥意义,就只是会在 /var/log/cron 里头记载该项任务的名称这样!通常与后续的目 录资源名称相同即可。
实际要进行的指令串:有没有跟 0hourly 很像啊!没错!相同的作法啊!透过 run-parts 来处理的!
根据上面的配置文件内容,我们大概知道 anacron 的执行流程应该是这样的 (以 cron.daily 为例):
1. 由 /etc/anacrontab 分析到 cron.daily 这项工作名称的天数为 1 天;
2. 由 /var/spool/anacron/cron.daily 取出最近一次执行 anacron 的时间戳;
3. 由上个步骤与目前的时间比较,若差异天数为 1 天以上 (含 1 天),就准备进行指令;
4. 若准备进行指令,根据 /etc/anacrontab 的设定,将延迟 5 分钟 + 3 小时 (看 START_HOURS_RANGE 的设定);
5. 延迟时间过后,开始执行后续指令,亦即 run-parts /etc/cron.daily 这串指令;
6. 执行完毕后, anacron 程序结束。
如此一来,放置在 /etc/cron.daily/ 内的任务就会在一天后一定会被执行的!因为 anacron 是每个小时被执行一次嘛! 所以,现在你知道为什么隔了一阵子才将 CentOS 开机,开机过后约 1 小时左右系统会有一小段时间的忙碌! 而且硬盘会跑个不停!那就是因为 anacron 正在执行过去/etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/ 里头的未进行的各项工作排程啦! 这样对 anacron有没有概念了呢? ^_^
最后,我们来总结一下本章谈到的许多配置文件与目录的关系吧!这样我们才能了解 crond 与anacron 的关系:
1. crond会主动去读取/etc/crontab, /var/spool/cron/*, /etc/cron.d/*等配置文件,并依据分、时、日、月、周的时间设定去各项工作排程;
2. 根据/etc/cron.d/0hourly 的设定,主动去/etc/cron.hourly/目录下,执行所有在该目录下的执行文件;
3. 因为/etc/cron.hourly/0anacron 这个脚本文件的缘故,主动的每小时执行 anacron ,并呼叫 /etc/anacrontab的配置文件;
4. 根据/etc/anacrontab的设定,依据每天、每周、每月去分析 /etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/内的执行文件, 以进行固定周期需要执行的指令。
也就是说,如果你每个周日的需要执行的动作是放置于 /etc/crontab 的话,那么该动作只要过期了就过期了,并不会被抓回来重新执行。 但如果是放置在 /etc/cron.weekly/ 目录下,那么该工作就会定期,几乎一定会在一周内执行一次~如果你关机超过一周, 那么一开机后的数个小时内,该工作就会主动的被执行喔! 真的吗?对啦!因为 /etc/anacrontab 的定义啦!
基本上,crontab 与 at 都是定时去执行,过了时间就过了!不会重新来一遍~那anacron 则是定期去执行,某一段周期的执行~ 因此,两者可以并行,并不会互相冲突啦!

本章总结

回到顶部

系统可以透过 at 这个指令来排程单一工作的任务! at TIME为指令下达的方法,当 at 进入排程后, 系统执行该排程工作时,会到下达时的目录进行任务;
at 的执行必须要有 atd 服务的支持,且 /etc/at.deny 为控制是否能够执行的使用者账号;
透过 atq, atrm 可以查询与删除 at 的工作排程;
batch 与 at 相同,不过 batch 可在 CPU 工作负载小于 0.8 时才进行后续的工作排程;
系统的循环例行性工作排程使用 crond 这个服务,同时利用 crontab -e 及 /etc/crontab 进行排程的安排;
crontab -e 设定项目分为六栏, 分、时、日、月、周、指令为其设定依据;
/etc/crontab 设定分为七栏, 分、时、日、月、周、执行者、指令为其设定依据;
anacron 配合 /etc/anacrontab 的设定,可以唤醒停机期间系统未进行的 crontab 任务!
5.6 本章习题
( 要看答案请将鼠标移动到答:
底下的空白处,按下左键圈选空白处即可察看 ) 简答题:
今天假设我有一个指令程序,名称为: ping.sh 这个文件名!我想要让系统每三分钟执行这个文件一次, 但 是偏偏这个文件会有很多的讯息显示出来,所以我的 root 账号每天都会收到差不多四百多封的信件,光是 收信就差不多快要疯掉了! 那么请问应该怎么设定比较好呢?
这个涉及数据流重导向的问题,我们可以将他导入文件或者直接丢弃!如果该讯息不重要的话, 那么就予以丢弃,如果讯息 很重要的话,才将他保留下来!假设今天这个命令不重要, 所以将他丢弃掉!因此,可以这样写:*/3 * * * * root /usr/local/ping.sh > /dev/null 2>&1
您预计要在 2016 年的 2 月 14 日寄出一封给 kiki ,只有该年才寄出!该如何下达指令?
at 1am 2016-02-14
下达 crontab -e 之后,如果输入这一行,代表什么意思?
* 15 * * 1-5 /usr/local/bin/tea_time.sh
在每星期的 1~5 ,下午 3 点的每分钟,共进行 60 次 /usr/local/bin/tea_time.sh 这个文件。 要特别注意的是,每个星期 1~5 的 3 点都会进行 60 次ㄟ!很麻烦吧~是错误的写法啦~ 应该是要写成:
30 15 * * 1-5 /usr/local/bin/tea_time.sh
我用 vi 编辑 /etc/crontab 这个文件,我编辑的那一行是这样的:
25 00 * * 0 /usr/local/bin/backup.sh
这一行代表的意义是什么?
这一行代表......没有任何意义!因为语法错误!您必须要了解,在 /etc/crontab 当中每一行都必须要有使用者才行!所以,应 该要将原本那行改成:
25 00 * * 0 root /usr/local/bin/backup.sh
请问,您的系统每天、每周、每个月各有进行什么工作?
因为 CentOS 系统默认 的例 行性命令都放置在 /etc/cron.* 里面,所以,你可以自行 去: /etc/cron.daily/, /etc/cron.week/, /etc/cron.monthly/ 这三个目录内看一看, 就知道啦! ^_^
每个星期六凌晨三点去系统搜寻一下内有 SUID/SGID 的任何文件!并将结果输出到 /tmp/uidgid.files vi /etc/crontab
0 3 * * 6 root find / -perm /6000 > /tmp/uidgid.files

initroot编辑整理,转载请注明www.initroot.com