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

at 仅执行一次,循环执行则是由 cron(cribd) 这个系统服务来控制的。该系统服务是默认启动的,同时也提供了例行性工作排程的指令 crontab。

# 使用者的设置

同样,为了安全考虑, crontab 指令也可以限制使用者账户:

  • /etc/cron.allow:在该文件中的用户才可以执行
  • /etc/cron.deny:在该文件中的用户,不能执行

一般保留一个文件来控制,系统是默认保留 /etc/cron.deny ,将禁止执行 crontab 的账户写入该文件,一行一个

当使用 crontab 指令建立工作后,会被记录到 /var/spool/cron/ 中去,而且是以账户来作为判断的。比如:mrcode 使用 crontab 后,他的工作会记录到 /var/spool/cron/mrcode 文件中,但是请注意,不要使用 vi 直接编辑该文件,因为可能由于输入语法错误,会导致无法执行 cron 。

另外 cron 执行的每一项工作都会被记录到 /var/log/cron 这个登录文件中,所以该日志文件,也可以在一定程度上检查一下是否有异常的执行,比如木马定时执行

# crontab 语法

crontab [-u username] [-l | -e | -r]

选项与参数:
	-u:只有 root 才能进行该任务,帮其他使用者建立/移除 crontab 工作排程
	-e:编辑 crontab 的工作内容
	-l:查询 crontab 的工作内容
	-r:移除所有的 crontab 的工作内容,若只删除一项,则使用 -e 编辑
1
2
3
4
5
6
7
# 范例 1:用 mrcode 身份在每天的 12:00 发信给自己
[mrcode@study ~]$ crontab -e
no crontab for mrcode - using an empty one
# 会进入 VI 的编辑画面,每一行都是一个工作
# 分 时 日 月 周 后面的则是指令串
0 12 * * * mail -s "at 12:00" mrcode < /home/mrcode/.bashrc

crontab: installing new crontab

1
2
3
4
5
6
7
8
9

每一行是一个工作,共有 6 个字段

代表意义 分钟 小时 日期 月份 指令
数字范围 0-59 0-23 1-31 1-12 0-7 指令串

特别注意:周的数字 0 或 7 都代表「星期天」,下面有一些辅助字符

特殊字符 含义
星号 * 任何时刻
逗号 , 表示分隔时段。比如 3:00 和 6:00 执行,那么 0 3,6 * * * command 第二栏中用逗号分隔,表示 每天的 3 点和 6 点都执行
减号 - 表示一段时间范围内。比如 8 点到 12 点之间,每小时的 20 分都执行一次,20 8-12 * * * command
斜线 /n n 表示数字,每隔 n 单位间隔。比如每五分钟执行一次,*/5 * * * * command,还可以写成 0-59/5 也是在这个范围类,每 5 分钟执行一次

下面进行练习,注意下,下面的练习需要使用 mrcode 这个账户来,后续的动作才能够搭配起来(可能和教学内容有关系吧,不同身份间的)

# 范例 1:假如你女朋友生日是 5.2,要在 5.1 23:59 发一封信给她,这封信的内容已经卸载 /home/mrcode/lover.txt 中了
crontab -e
59 23 1 5 * mail kiki < /home/mrcode/lover.txt

# 范例 2:每 5 分钟执行一次  `/home/mrcode/test.sh`
*/5 * * * * sh /home/mrcode/test.sh
1
2
3
4
5
6

crontab 每个人都有一个文件存在于 /var/spool/cron/ 目录中,指令下达时,最好使用绝对路径

# 范例 3:在每周 5 下午 4:30 告诉你朋友,周 6 的聚会会准时去
30 16 * * 5 mail friend@server.name < /home/mrcode/friend.txt
1
2
# 查询已经存在的
[mrcode@study ~]$ crontab -l
59 23 1 5 * mail kiki < /home/mrcode/lover.txt
*/5 * * * * sh /home/mrcode/test.sh
30 16 * * 5 mail friend@server.name < /home/mrcode/friend.txt

# 移除工作,只移除一个的话,必须使用 crontab -e 手动删除一行
# 如果是全部移除的话 -r 参数
[mrcode@study ~]$ crontab -r
[mrcode@study ~]$ crontab -l
no crontab for mrcode

1
2
3
4
5
6
7
8
9
10
11
12

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

crontab -e 是针对使用者的 cron 来设计的,如果是系统的定时任务,则是编辑 /etc/crontab 文件来配置的,crontab -e 指令是 /usr/bin/crontab

基本上 cron 服务最低频率是分钟,所以 cron 每分钟去读一次 /etc/crontab/var/spool/cron/* 内容,因此修改完文件内容后,等待下一分钟就可以生效了,但是还有一种情况是因为某些原因或则是其他的 Unix 系统中,crontab 是读入到内存中的,所以编辑文件后,并不会立即生效,如果是这种情况,重启 crond 服务就好了 systemctl restart crond

# /etc/crontab

[root@study ~]# cat /etc/crontab 
SHELL=/bin/bash		# 使用哪种 shell 接口
PATH=/sbin:/bin:/usr/sbin:/usr/bin		# 执行文件搜索路径
MAILTO=root				# 若有额外 STDOUT 以 email 将数据发送给谁

# For details see man 4 crontabs

# 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

其他的内容则和 crontab -e 中的类似

  • MAILTO=root:当 /etc/crontab 文件中的工作指令发送错误时或该工作执行结果有 STDOUT/SEDERR 时,会将错误信息或屏幕显示结果发送给谁?默认由系统寄发一封 mail 给 root,不过偶遇 root 无法在客户端中以 POP3 之类的软件收信,这里一般可以写一个邮箱

  • PATH:指令搜索路径

  • 分 时 日 月 周 身份 指令 七个字段的设置

    这里比 /etc/crontab 多了一个 身份字段。由于使用者自己的 crontab 并不需要指令身份,但是 /etc/crontab 里面是需要的,如果不指定则默认以 root 身份

# crond 服务读取配置文件的位置

一般来说 ,crond 预设有三个地方会有执行脚本配置文件:

  • /etc/crontab:系统级
  • /etc/cron.d/*:系统级
  • /var/spool/cron/*:与使用者有关
[root@study ~]# ls -l /etc/cron.d
total 12
-rw-r--r--. 1 root root 128 Aug  9  2019 0hourly
-rw-r--r--. 1 root root 108 Aug  6  2019 raid-check
-rw-------. 1 root root 235 Aug  9  2019 sysstat
# 书上说有 4 个文件,这里少了一个 unbound-anchor 的文件

[root@study ~]# 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
1
2
3
4
5
6
7
8
9
10
11
12
13

cron.d 目录下存放的是 crontab 的配置文件脚本,一般来说,你想要自己开发新的软件,该软件要拥有自己的 crontab 定时指令时,就以如上 0hourly 文件的方式,放置到该目录下

TIP

举例来说 cron.d 目录的使用场景;开发了一个虚拟化教室的软件,该软件需要定时清除一些垃圾防火墙规则,那么编写一个配置文件 /etc/cron.d/newfile ,如果将来该软件升级,直接覆盖之前的文件即可,比手动去分析 /etc/crontab 要方便

/etc/cron.d/0hourly 文件中,配置的是 每个整点 1 分的时候会执行 run-parts /etc/cron.hourly

[root@study ~]# type run-parts
run-parts is /bin/run-parts
1
2

会发现,run-parts 是一个 shell 脚本文件,该脚本的工作内容大概是: 5 分钟左右选一个时间来执行 /etc/cron.hourly 目录内的所有执行文件,因此,放在 /etc/cron.hourly 的文件,必须是能被直接执行的指令脚本

也就是说,除了时分日月周加上指令路径的 crond 配置文件之外,也可以把你的 sh 脚本放在 /etc/cron.hourly 目录下,该目录下的文件,将在每小时 1 分钟后 5 分钟内,随机选一个时间点来执行

[root@study ~]# ls -d /etc/cron.*
/etc/cron.d  /etc/cron.daily  /etc/cron.deny  /etc/cron.hourly  /etc/cron.monthly  /etc/cron.weekly

1
2
3

除了 cron.hourly 之外,还有 monthly、weekly、deny 目录,分别代表了每月、每周、每日执行一次,这三个目录与 hourly 不一样,他们三个是由 anacron 执行的,而 anacron 的执行方式是放在 /etc/cron.hourly/0anacron 里面的,与前几代 anacron 是单独的 service 不太一样(后续讲解,笔者猜测前几代的 anacron 是一个系统服务,而这里是使用定时任务来达成的)

# 小结

  • 个人化的行为使用 crontab -e:由于 /etc/crontab 是大家都能够读取的权限,放在这个文件中就没有隐私了

  • 系统维护管理使用 vim /etc/crontab:如果是系统的重要工作,为了自己管理方便和追踪,建议写入该文件中

  • 固定每小时、每日、每周、每天执行的特别工作

    如果与系统维护有关,还是建议放到 /etc/crontab 中集中管理较好。如果想要偷懒,这个几个快捷的目录能满足的话,就放到这些目录中去

# 一些注意事项

防止所有任务都在同一个时间点执行,分散时间点,让系统资源更好的被利用,有以下几个点:

  • 资源分配不均的问题

    最严重的问题是系统资源分配不均的问题,举个例子,检测主机流量的信息包括:

    • 流量
    • 区域内其他 PC 的流量检测
    • CPU 的使用率
    • RAM 使用率
    • 在线人数事实检测

    如果每个流程都在同一个时间启动的话,那么在某个时间段,系统会变得相当频繁,所以需要分别错开他们执行时间

  • 取消不要的输出项目

    有一个困扰是:当有执行结果或执行中有输出的数据时,该数据会 mail 给 MAILTO 的设置账户,那么当一个任务一直出错(例如 DNS 的检查中,当 DNS 上层主机挂掉,那么你就会一直受到错误信息),想要不看到这些错误的邮件,就可以使用数据重导向将输出结果输出到 /dev/null

  • 安全的校验

    很多时候被植入木马都是以定时任务的方式来植入的,可以检查 /var/log/cron 的内容来检查是否有异常的定时任务运行记录(比如一个你没有见过的定时任务)

  • 周与日月不可同时并存