Linux 之旅 14:任务计划(crontab)

本文详细介绍了Linux系统中的任务计划工具,包括一次性任务工具at和周期性任务工具crontab。at通过服务进程atd执行一次性任务,而crontab用于设置周期性任务,如日志分析、数据库更新等。文章还提到了任务计划的权限管理和执行机制,以及如何通过anacron处理错过执行时间的任务。
摘要由CSDN通过智能技术生成

Linux 之旅 14:任务计划(crontab)

image-20210825152156502

图源:pexels

什么是任务计划

事实上,计算机最便利的事情就是帮我们自动化的完成一些工作,比如追新番的时候自动下载最新一集的动漫。但是一些比较复杂的自动化流程往往需要复杂的设定,几乎就等同于在编程,相比之下制定一些定时任务就简单多了,比方说设定家里的电视定时开机,扫地机器人定时开始清扫之类的。

而Linux也具备执行定时任务的能力。

当然Windows也可以执行定时任务,但是从我使用的情况来看,还是不要折腾的比较好。

Linux任务计划的种类

Linux上可以通过两种命令(工具)来制定任务计划:

  • at:可以制定一次性任务,由服务进程atd负责执行。
  • crontab:可以制定周期性任务,有服务进程crond负责执行。

CentOS上常见的例行性工作

CentOS上有一些已经设定好的周期性任务:

  • 执行日志文件的轮询(logrotate)

    会定时地检查和整理日志文件,将新日志和旧日志分别存放。

  • 日志文件分析(logwatch)

    程序logwatch会定期分析登录相关地日志,并发送标题为logwatch的邮件。

  • 更新locate数据库

    之前说过,locate命令查询依赖于数据库,所以系统会定期更新locate数据库。

  • 更新manpage查询数据库

    同样,man命令依赖于manpage查询数据库,所以也需要定期扫描,以将新安装的软件的manpage信息更新入数据库中。

  • 更新PRM数据库

    PRM是CentOS的包管理工具,如果不是通过PRM安装的软件,PRM的数据库中是查不到相应信息的,所以同样要定期执行更新操作。

  • 删除缓存

    随着程序的运行,会基类越来越多的缓存和临时文件,所以需要定期执行缓存的清理工作。

  • 与网络服务有关的分析操作

    如果安装了网络服务(Apache等),同样会附带一些定时任务。

仅执行一次的计划任务

atd的启动与at运行方式

命令at的执行依赖于服务进程atd,所以我们先要确认是否已经启用该服务:

[icexmoon@xyz ~]$ systemctl status atd
● atd.service - Job spooling tools
   Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
   Active: active (running) since 三 2021-08-25 15:42:49 CST; 1min 1s ago
 Main PID: 1264 (atd)
    Tasks: 1
   CGroup: /system.slice/atd.service
           └─1264 /usr/sbin/atd -f

825 15:42:49 xyz.icexmoon.centos systemd[1]: Started Job spooling tools.

systemctl用于管理服务进程,以后会详细介绍。

这里的Active: active (running)说明服务已经在正常运行中了,而后边的atd.service; enabled说明该服务会开机自动启动。

如果进程没有运行,或者不是开机自动启动,可以:

[root@xyz ~]# systemctl restart atd
[root@xyz ~]# systemctl enable atd
[root@xyz ~]# systemctl status atd
● atd.service - Job spooling tools
   Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
   Active: active (running) since 三 2021-08-25 15:47:48 CST; 18s ago
 Main PID: 2116 (atd)
   CGroup: /system.slice/atd.service
           └─2116 /usr/sbin/atd -f

825 15:47:48 xyz.icexmoon.centos systemd[1]: Started Job spooling tools.
at的运行方式

事实上,当我们通过at创建一次性定时任务时,会在/var/spool/at/目录下生成一个包含命令内容的文本文件,服务进程atd会读取该文件并在合适的时候并执行。

所有的定时任务创建都属于高危权限,所以需要进行权限设置和管理,at通过两个文件来控制权限:

  • /etc/at.allow:相当于白名单,只要写入这里的账户都可以执行at,反之则不能。
  • /etc/at.deny:相当于黑名单,只要写入这里的账户都不能执行at,反之则可以。

这里的两个配置文件类似于黑白名单,且白名单的优先级高于黑名单,如果同时两个配置文件都存在,则白名单生效,黑名单不会起作用。

实际运行单一任务计划

最常见的是创建一个若干时间后执行的任务:

[root@xyz ~]# at now + 5 minutes
at> mail -s 'test at' root
at> a email test the at command
at> .
at> <EOT>
job 1 at Wed Aug 25 16:01:00 2021

at后跟设定的时间,使用Enter后换行,会出现at>这个标识符,之后就可以像正常输入Bash命令那样输入了,输入完毕后需要使用Ctrl+D以结束输入。

定时任务创建后会输出一行任务相关的信息:job 1 at Wed Aug 25 16:01:00 2021。其中job 1是给任务分配了一个at中的编号,之后的时间就是任务将会执行的时间。

使用任务编号我们可以通过at查询任务内容:

[root@xyz ~]# at -c 1
#!/bin/sh
# atrun uid=0 gid=0
# mail icexmoon 0
umask 22
XDG_SESSION_ID=1; export XDG_SESSION_ID
...省略...
LESSOPEN=\|\|/usr/bin/lesspipe.sh\ %s; export LESSOPEN
cd /root || {
         echo 'Execution directory inaccessible' >&2
         exit 1
}
${SHELL:-/bin/sh} << 'marcinDELIMITER0a436847'
mail -s 'test at' root
a email test the at command
.

marcinDELIMITER0a436847

可以看到实际上是创建了一个包含指定命令的Bash脚本,atd会以执行该脚本的方式执行命令。

如果想在一个确切的时间点执行任务:

[root@xyz ~]# at 23:00 2021-08-25
at> sync
at> sync
at> shutdown -h now
at> <EOT>
job 2 at Wed Aug 25 23:00:00 2021

通过at创建的任务将由服务进程atd执行,所以和用户当前的终端是没有关系的,终端注销或断开连接不会影响到任务的执行,并且任务执行后输出的正常信息和错误信息也不会在当前终端中显示,而是会以邮件的形式发送到创建用户的mailbox中。

如果一定需要将结果输出到当前终端,可以使用stdout重定向:

[root@xyz ~]# finger
Login     Name           Tty      Idle  Login Time   Office     Office Phone   Host
icexmoon  魔芋红茶   pts/0          Aug 25 15:43 123456     123456         (icexmoon-book)
[root@xyz ~]# at now + 1 minutes
at> echo 'test at' > /dev/pts/0
at> <EOT>
job 4 at Wed Aug 25 16:24:00 2021
[root@xyz ~]# test at

[root@xyz ~]#
  • 终端tty1pts/0都以文件的形式存在于/dev/目录中,所以可以当作文件来重定向输出。
  • 如果at创建的任务执行后不会产生输出,则也不会有邮件发送给用户,如果无论如何也要发送邮件,可以使用at -m的方式创建任务。
at任务的管理

如果需要删除已存在的at任务,可以:

[root@xyz ~]# atq
2       Wed Aug 25 23:00:00 2021 a root
[root@xyz ~]# at -c 2 | less
[root@xyz ~]# atrm 2
[root@xyz ~]# atq

可以先用atq查询已经存在的at任务列表,然后使用at -c 2 | less的方式查看任务的具体内容,如果的确不需要了,可以使用atrm进行删除。

事实上atqatrm都是at命令带指定参数的别名,可以使用man at查看详情。

batch

batch实际上也是通过at来执行定时任务,不同的是通过加入一些控制参数,可以实现特殊的用途,比如:在CPU的任务负载小于0.8时才执行任务。

所谓的CPU任务负载,其实就是同一时间内排队等待CPU执行的任务数,一般来说时间单位是分钟,并且会取最近一段时间的平均值,所以会出现0.8这样的小数。

下面进行模拟,先通过添加一些后台任务拉高CPU负载:

[root@xyz ~]# echo 'scale=100000; 4*a(1)' | bc -lq &
[1] 2801
[root@xyz ~]# echo 'scale=100000; 4*a(1)' | bc -lq &
[2] 2803
[root@xyz ~]# echo 'scale=100000; 4*a(1)' | bc -lq &
[3] 2805
[root@xyz ~]# echo 'scale=100000; 4*a(1)' | bc -lq &
[4] 2807

命令结尾添加&可以让命令在后台执行,相关详细介绍在下篇内容。

过十秒左右使用uptime命令可以观察到CPU负载升高:

[root@xyz ~]# uptime
 16:42:21 up 59 min,  1 user,  load average: 2.92, 0.94, 0.37

此时可以使用batch添加一个CPU低负载时候才会执行的任务:

[root@xyz ~]# batch
at> updatedb
at> <EOT>
job 5 at Wed Aug 25 16:42:00 2021
[root@xyz ~]# date;atq
2021年 08月 25日 星期三 16:42:54 CST
5       Wed Aug 25 16:42:00 2021 b root

可以看到任务执行时间已经过了,但任务还没有被执行。

现在需要杀死后台job以降低CPU负载:

[root@xyz ~]# jobs
[1]   运行中               echo 'scale=100000; 4*a(1)' | bc -lq &
[2]   运行中               echo 'scale=100000; 4*a(1)' | bc -lq &
[3]-  运行中               echo 'scale=100000; 4*a(1)' | bc -lq &
[4]+  运行中               echo 'scale=100000; 4*a(1)' | bc -lq &
[root@xyz ~]# kill -9 %1 %2 %3 %4
[root@xyz ~]# jobs
[1]   已杀死               echo 'scale=100000; 4*a(1)' | bc -lq
[2]   已杀死               echo 'scale=100000; 4*a(1)' | bc -lq
[3]-  已杀死               echo 'scale=100000; 4*a(1)' | bc -lq
[4]+  已杀死               echo 'scale=100000; 4*a(1)' | bc -lq

jobskill等相关命令会在下篇文章进行介绍。

接下来可以不断使用uptime;atq查看CPU负载和at任务队列中的任务:

[root@xyz ~]# uptime;atq
 16:44:21 up  1:01,  1 user,  load average: 2.03, 1.49, 0.65
5       Wed Aug 25 16:42:00 2021 b root
[root@xyz ~]# uptime;atq
 16:44:25 up  1:01,  1 user,  load average: 1.87, 1.47, 0.65
5       Wed Aug 25 16:42:00 2021 b root
...省略...
[root@xyz ~]# uptime;atq
 16:48:21 up  1:05,  1 user,  load average: 0.05, 0.68, 0.51

当前一分钟的负载低于0.8时,任务就会被执行。

uptime显示的三个负载值load average:分别代表前1分钟、5分钟、15分钟的平均任务负载。

循环执行的计划任务

设置用户计划任务

用户想要创建定期执行的任务(一周、一个月、一年等),可以使用crontab命令。

因为之前说过的,定时任务创建是一个敏感权限,所以同样的,crontab也有类似于at那样的黑白名单权限控制:

  • /etc/cron.allow:白名单,存在的用户名可以使用crontab,反之则不能。
  • /etc/cron.deny:黑名单,存在的用户不可以使用crontab,反之则可以。

黑白名单同时存在时,黑名单不起作用,这点和at是一样的。

使用白名单可以看作是严格机制,黑名单则可以看作是一种宽松机制。

实际上用户通过crontab命令修改的定时任务配置文件是保存在/var/spool/cron目录下的:

[root@xyz ~]# ll /var/spool/cron/
总用量 4
-rw-------. 1 icexmoon icexmoon 107 825 17:08 icexmoon

虽然该文件的拥有者是当前用户,但是目录/var/spool/cron本身的权限却是drwx------. 2 root root,所以实际上用户是没有办法直接修改该文件的,只能通过crontab命令来间接修改。

设置定时任务也很简单:

[icexmoon@xyz ~]$ crontab -e
#分钟   小时    日      月      周      命令
10      17      *       *       *       mail -s 'crontab test' icexmoon < /home/icexmoon/test.py

时间设定的格式为分钟 小时 日 月 周,其中分钟的范围为0~59,小时为0~23,日为1~31,月为1~12,周比较独特,是0~7,其中07都表示周日。

还有几个比较特殊的符号可以使用:

  • *:类似于通配符的用法,可以代表任何值,比如上边的10 17 * * *就表示任何月任何日的17点10分(其实就是每天的17:10)。
  • ,:可以指定多个值,比如1,5,10,15 * * * *就可以表示每个小时的1、5、10、15分钟。
  • -:可以指定一个范围,比如0 12 1-3 * *就表示每个月的前三天的12点整。
  • /n:可以表示一个时间间隔,比如0/20 * * * *就表示每个小时的整点、20分、40分。

/n的方式其实相当于编程中的整除,也就是说只要能被整除除净的值,都是可以触发任务的合法时间。

要查看当前账户设置的定时任务,可以:

[icexmoon@xyz ~]$ crontab -l
#分钟   小时    日      月      周      命令
10      17      *       *       *       mail -s 'crontab test' icexmoon < /home/icexmoon/test.py

要删除某条任务计划,可以使用crontab -e直接删除该条记录,或者使用#注释掉。如果要删除全部的任务计划,可以:

[icexmoon@xyz ~]$ crontab -r
[icexmoon@xyz ~]$ crontab -l
no crontab for icexmoon

设置系统计划任务

如果要设置系统的计划任务,需要修改配置文件/etc/crontab

在某些Linux发行版中,服务进程crond会将配置文件/etc/crontab加载到内存中用于执行计划任务,这种情况下修改配置文件/etc/crontab后并不能让计划任务立即生效,必须重启crond以重新加载配置文件:systemctl restart crond

这个文件内容长这样:

[icexmoon@xyz ~]$ cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# 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

SHELL表示执行命令时使用的shellPATH表示使用的PATH环境变量,MAILTO表示将执行结果用邮件发送给哪个用户。再下面就是计划任务的设置了,形式与用户计划任务类似,不过多出一个设置项“user-name”,因为用户计划任务执行的时候肯定是使用该用户的账户身份执行,所以不需要指定账户,但系统计划任务就必须要指定一个账户,作为执行该任务时使用的身份了。

系统相关的计划任务除了可以写入/etc/crontab配置文件,还可以放在/etc/cron.d目录下:

[icexmoon@xyz ~]$ ll /etc/cron.d
总用量 12
-rw-r--r--. 1 root root 128 89 2019 0hourly
-rw-r--r--. 1 root root 108 930 2020 raid-check
-rw-------. 1 root root 235 41 2020 sysstat
[icexmoon@xyz ~]$ 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

通过查看0hourly这个文件可以发现,其实内容和/etc/crontab是相似的,也就是说我们可以编写同样的配置文件,放在/etc/cron.d中,同样会被服务进程crond执行。

此外0hourly配置文件中的run-parts命令可以在5分钟之内随机选择一个时间执行气候指定的目录中的可执行文件。也就是说每小时的1~5分钟内,/etc/cron.hourly目录中的可执行文件会被执行。

所以如果我们有系统相关的shell脚本需要每个小时执行一次,就可以放置在/etc/cron.hourly目录中。

类似的,/etc目录中还有cron.dailycron.weeklycron.monthly目录,同样用于放置需要周期性执行的可执行文件,不过它们是由anacron而非crontab执行,所以执行机制和效果略区别。

虽然除了/etc/crontab配置文件,许多地方都可以放置计划任务,但是依然建议尽量将任务计划集中在/etc/crontab中集中管理。

特别的,如果你在Linux上开发程序和应用,且需要设置计划任务,则应该在/etc/cron.d目录下创建配置文件。

一些注意事项

有一些计划任务相关的注意事项:

  • 资源分配不均

    需要尽量避免在同一个时间点执行大量长时间运行的任务,可以通过合理安排,让计划任务错开时间执行。

  • 屏蔽不必要的输出

    默认情况下如果任务计划会输出信息,则会发邮件给MAILTO指定的账户,如果不希望受到该任务的信息,可以使用stdout重定向,重定向到文件或者/dev/null,后者将吞掉所有的内容。

  • 安全的检验

    黑客往往会通过添加计划任务的方式定期手机敏感信息,以进一步加以利用,所以需要检查/var/log/cron中的计划任务执行日志来检查是否有非正常用户设定的计划任务被执行。

  • 周与日月不可同时使用

    周往往应当和模糊日期结合使用,比如“每个星期五”之类的,如果结合确切的日期,比如9月11日 星期五这种,可能会造成不可预计的后果。

可唤醒停机期间的工作任务

我们之前说的crontab方式创建的任务计划,是针对时间点设置的定期执行任务,这存在一个致命问题,即如果到了需要执行任务的时间点,但是Linux主机无法执行任务(比如非正常关机),任务就会错过被执行的机会,只能等到下一个触发的时间点。如果是短时间任务还好说,如果是长时间间隔的任务,比如说每个月执行一次的,就很致命了。

所以我们需要一个即使错过执行时间,也可以在系统重新启动后执行任务的机制。

anacron就可以做到。

什么是anacron

anacron存在的意义上边已经说过了,anacron实际上也是通过crond进行触发的,每个小时触发一次。

anacron启动后会根据比对其上次执行的时间来确定是否应该对相关的计划任务进行执行。比如说某个可执行文件存在于/etc/cron.daily目录下,理论上每天至少应当执行一次,但是Linux主机关机了2天,开机后anacroncrond触发,然后检查到自己上次执行时间已经是2天前了,自然会将/etc/cron.daily下的可执行文件都执行一遍。

anacron 与 /etc/anacrontab

上面已经说过了,anacroncrond每小时触发一次,准确地说,是通过/etc/cron.hourly/0anacron这个脚本触发:

[icexmoon@xyz cron.hourly]$ cat 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

# 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的执行时间,如果是同一天内执行的,则直接结束脚本,否则继续检查Linux主机的供电是否正常(on_ac_power),如果不正常,则结束脚本,如果正常,则执行anacron -s

anacron -s会根据之前的执行时间进一步判断其下的任务计划是否应该被执行。

anacron的核心配置文件是/etc/anacrontab

[icexmoon@xyz cron.hourly]$ sudo cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

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

anacron会依据配置中的period in days这个配置项并结合上次执行的记录决定是否执行相关命令。

上次的相关执行记录可以这样查看:

[icexmoon@xyz cron.hourly]$ sudo more /var/spool/anacron/*
::::::::::::::
/var/spool/anacron/cron.daily
::::::::::::::
20210825
::::::::::::::
/var/spool/anacron/cron.monthly
::::::::::::::
20210729
::::::::::::::
/var/spool/anacron/cron.weekly
::::::::::::::
20210820

这里的记录表明cron.daily这个标识的anacron任务上次执行的时间为2021年8月25日,cron.monthly这个标识的anacron任务上次执行时间为2021年7月29日,以此类推。

/etc/anacron配置文件结合起来就可以决定是否应当执行任务,比如:

cron.daily这个任务的period in days是1,而上次执行时间是20210825,则说明不需要执行该任务。如果上次执行时间是20210824,则会执行任务,即run-parts /etc/cron.daily,这意味着/etc/cron.daily目录下所有的可执行文件都会被执行。

当然,即使任务的确需要执行,anacron也不会一股脑同时执行,为了避免集中执行任务,就会使用配置文件中delay这个设置,不同的任务会延迟不同的时间后开始执行。

正因为anacron具有上边的特性,所以我们可以将“错过时间也应当执行的周期性任务”通过anacron执行。具体方式即可以简单地将可执行文件放置在/etc/cron.daily之类的anacron预定义的目录下,也可以自行编辑/etc/anacron配置文件,以满足一些个性化的需求。

关于任务计划的内容介绍完毕,谢谢阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值