简介
计划任务,或者调度任务,在现实生活随处可见。Java编程中的任务调度参考分布式调度中心学习总结。Linux或者Windows系统也有任务调度的概念,本文尽可能涵盖这个主题。
windows的计划任务,在控制面板 -> 性能与维护 -> 任务计划,用于安排自动运行的任务。通过’添加任务计划’的一步步引导,可建立一个定时执行的任务。
Linux下实现任务调度一般需要一个与命令行工具相对应的服务。这样的命令行工具包括:at、crontab、anacron、Systemd。
at
使用 at 命令可以调度任务的自动执行。at 文件只执行一次任务,然后便从目录中删除这些文件。故而常用于在运行将输出定向到独立文件中以供以后检查的单个命令或脚本。
at 命令在/var/spool/at/
目录中存储您运行的命令或脚本以及当前环境变量的副本。at 作业文件名是一个长数字,用于指定该文件在 at 队列中的位置,后跟 .a
扩展名。at指定一个时间执行某个指定任务,只能执行一次,且需要开启atd进程,通过命令ps -ef | grep atd
查看, 开启用/etc/init.d/atd start or restart
;设置开机即启动运行chkconfig --level 2345 atd on
。
选项:
-m 当指定的任务被完成之后,将给用户发送邮件,即使没有标准输出
-I atq的别名
-d atrm的别名
-v 显示任务将被执行的时间
-c 打印任务的内容到标准输出
-V 显示版本信息
-q<列队> 使用指定的列队
-f<文件> 从指定文件读入任务而不是从标准输入读入
-t<时间参数> 以时间参数的形式提交要运行的任务
at允许使用一套相当复杂的指定时间的方法:
- 绝对计时法:
接受在当天的hh:mm式的时间指定,假如该时间已是过去时,则放在第二天执行
接受midnight,noon,teatime等比较模糊的词语来指定时间
接受12小时计时制,即在时间后面加上AM或PM
接受具体日期如:month day
或mm/dd/yy
或dd.mm.yy
。指定的日期必须跟在指定时间的后面 - 相对计时法:
格式为:now + count time-units ,now就是当前时间,time-units是时间单位,如minutes、hours、days、weeks。count是时间的数量。
或者直接使用today、tomorrow来指定完成命令的时间。
汇总表格
时间格式 |
说明 |
HH:MM |
like 12:12, if time is already past, will be executed at the specified next day |
midnight |
12:00 a.m. |
noon |
12:00 p.m. |
teatime |
4:00 p.m. |
month-name day year |
like May 1 2018, year is optional |
MMDDYY, MM/DD/YY, MM.DD.YY, HH:MM YYYY-MM-DD formats |
111218, equals 2018/11/12, 04:00 2009-03-17 |
now+time |
time can be in minutes, hours, days or weeks, like now + 5hours |
|
like 04pm + 3 days |
|
04pm March 17 |
17:20 tomorrow |
specific time |
实例:
# 启用at任务服务
systemctl start atd
systemctl enable atd
# 查看当前用户的所有任务(即还没有执行的任务)
atq 或者 at -l
# 查看所有用户的任务,展示出所有排定好的任务,且每个任务都有一个job ID
sudo atq
# 删除编号为3的任务
atrm 3或者at -d 3
# 设置1分钟后执行指定命令
echo 'echo aaa > /var/han' | at now+1min
at 12:00 PM September 30 2017
at 9:00 AM tomorrow
# 显示已经设置的任务内容
at -c 8
输出:
#!/bin/sh
# atrun uid=0 gid=0
# mail root 0
umask 22此处省略n个字符
date >/root/2013.log
atd 的启动
并非所有的 Linux发行版都默认会把atd服务打开。
启动atd服务:/etc/init.d/atd start
,其他参数:stop|restart|condrestart|status
配置开机时启动服务,免得每次重新启动都得再来一次:chkconfig atd on
at 的运行方式
使用 at 这个命令来产生所要运行的计划任务,并将这个计划任务以文字档的方式写入/var/spool/at/
目录内,该工作便能等待 atd 这个服务的取用与运行。
安全
很多主机被所谓的攻击破解后,经常发现系统中有很多的黑客程序,这些程序非常可能运用一些计划任务来运行或搜集你的系统运行信息,并定时的发送给黑客。 因为考虑到系统安全的原因。
通过/etc/at.allow
与/etc/at.deny
两个文件来限制 at 的使用的具体规则:
-
/etc/at.allow
文件:只有写在这个文件中的使用者才能使用 at,(即使没有写在at.deny
当中) -
/etc/at.allow
不存在,就寻找/etc/at.deny
文件,若写在这个at.deny
的使用者则不能使用 at ,而没有在这个at.deny
文件中的使用者,就可以使用 at 命令。 - 两个文件都不存在,只有 root 可以使用 at 这个命令。
/etc/at.allow
是管理较为严格的方式,而/etc/at.deny
则较为松散。在一般的 distributions 当中,由于假设系统上的所有用户都是可信任的, 因此系统通常会保留一个空的/etc/at.deny
文件,意思是允许所有人使用 at 命令的意思。一个帐号写一行。
crontab
Linux系统下最常用的命令行工具,也是Linux系统最基础内容其一。
内容较多,影响阅读体验。另起一篇Linux学习之定时任务调度cron/crontab
anacron
anacron与cron的不同点
- cron任务同通过常驻的守护进程crond来定期执行的。 而anacron则不是守护进程,它需要被别人定期掉起,比如跟cron或systemd timer配合
- crond每分钟检查一次是否有需要执行的任务,若这次任务错过了时间则需要等下次触发点才能再次执行。 而anacron会立即执行错过时间的任务,而正是由于这个特点,为了防止在不恰当的时刻执行命令,anacron专门有一个参数START_HOURS_RANGE来设置允许执行命令的时间段。
- cron的粒度能精确到分钟,而anacron的粒度只能到天(即执行频率不能超过1天1次)
- cron的执行时间很精确。anacron可以设置一个延迟时间(RANDOMDELAY),anacron会在这个延迟时间内的某一个随机时间点执行。好处在于可以让任务的执行时间分散,防止一瞬间对服务器产生太大压力。
- cron有多个设置任务的地方(
/var/spool/cron/<user>
,/etc/crontab
,/etc/cron.d/*
);anacron只有一个设置任务的地方(默认为/etc/anacrontab
,通过 -t
设置其他路径配置文件)
anacron其实并不是设计来定时执行任务的,它的主要功能还是为了缓解cron中一瞬间大量任务并发执行而导致系统压力过大的问题。
/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
跟 cron 的配置文件类似。都是先定义环境变量,再定义执行计划。
定义环境变量的部分跟cron一样:
- RANDOME_DELAY=45:表示anacron在执行任务前先延时一段随记的时间再执行,这段随机的时间为0-45分钟之内的随机数。
- START_HOURS_RANGE=3-22:指定只有在凌晨3点到晚上22点这个时间段内才允许执行任务。
定义执行计划部分就跟 cron 差别很大。执行计划由四部分组成:
- period in days:轮回天数,表示任务多少天执行一次。
- delay in minutes:表示启动Anacron和运行作业时间之间的延迟,单位为分钟。前提是自最后一次运行之后所经过的时间超出了轮回天数。并不是作业真正运行的时间,真正运行的时间还需要加上RANDOME_DELAY中设置的随机分钟数。
- job-identifier:作业的标识符。anacron在执行任务时会将日期写入
/var/spool/anacron/$job-identifier
文件中 - command:实际运行的命令。
run-parts
是一个运行指定目录中所有程序与脚本的命令,通过man run-parts
查看说明
anacron常用选项:
-t:用于配置文件路径,让anacron从指定配置文件中读取配置,常用于普通用户来管理个人任务
-S:即spooldir,设定新的spool目录来记录任务执行的时间戳,常用于普通用户来管理个人任务
-T:测试配置文件是否正确(类似nginx -t
)
-f:强制执行所有的任务,而不管之前的执行日期是否超过轮回天数
-u:将所有任务的执行日期都更新为当前日期,而不真正的执行任务
-s:串行地执行任务,只有前一个任务完成后才开始下一个任务。
Systemd
Systemd作为 Linux 的系统启动器,功能强大:
- 自动生成日志,配合 Systemd 的日志工具journalctl,方便除错;
- 可以设置内存和 CPU 的使用额度,比如最多使用50%的 CPU;
- 任务可以拆分,依赖其他 Systemd 单元,完成非常复杂的任务。
单元
单元是 Systemd 的最小功能单位,是单个进程的描述。Systemd 的基本思想就是一个个小的单元互相调用和依赖,组成一个庞大的任务管理系统。大概一共有12种单元:Service 单元负责后台服务,Timer 单元负责定时器,Slice 单元负责资源的分配。每个单元都有一个单元描述文件,它们分散在三个目录。
/lib/systemd/system:系统默认的单元文件
/etc/systemd/system:用户安装的软件的单元文件
/usr/lib/systemd/system:用户自己定义的单元文件
新建Service,就是在/usr/lib/systemd/system
目录里面新建一个文件。mytimer.service文件示例:
[Unit]
Description=MyTimer
[Service]
ExecStart=/bin/bash /path/to/mail.sh
- [Unit]部分介绍本单元的基本信息(即元数据),Description字段给出单元的介绍(名字叫做MyTimer)。
- [Service]部分用来定制行为,Systemd 提供许多字段。
ExecStart:systemctl start所要执行的命令
ExecStop:systemctl stop所要执行的命令
ExecReload:systemctl reload所要执行的命令
ExecStartPre:ExecStart之前自动执行的命令
ExecStartPost:ExecStart之后自动执行的命令
ExecStopPost:ExecStop之后自动执行的命令
所有定义路径都要写成绝对路径,如 bash 要写成 /bin/bash,否则 Systemd 会找不到。
Service 单元只是定义如何执行任务,要定时执行这个 Service,还必须定义 Timer 单元。/usr/lib/systemd/system目录下新建一个mytimer.timer文件:
[Unit]
Description=Runs mytimer every hour
[Timer]
OnUnitActiveSec=1h
Unit=mytimer.service
[Install]
WantedBy=multi-user.target
这个 Timer 单元文件分成几个部分:
- [Unit]部分定义元数据。
- [Timer]部分定制定时器
OnActiveSec:定时器生效后,多少时间开始执行任务;
OnBootSec:系统启动后,多少时间开始执行任务;
OnStartupSec:Systemd 进程启动后,多少时间开始执行任务;
OnUnitActiveSec:该单元上次执行后,等多少时间再次执行;
OnUnitInactiveSec: 定时器上次关闭后多少时间,再次执行;
OnCalendar:基于绝对时间,而不是相对时间执行;
AccuracySec:设置任务推迟执行的最大秒数,默认是60秒;
Unit:真正要执行的任务,默认是同名的带有.service后缀的单元;
Persistent:设置该字段后,即使定时器到时没有启动,也会自动执行相应的单元;
WakeSystem:如果系统休眠,是否自动唤醒系统;
OnUnitActiveSec=1h 表示一小时执行一次任务。其他的写法还有 OnUnitActiveSec=-- 02:00:00 表示每天凌晨两点执行,OnUnitActiveSec=Mon – 02:00:00 表示每周一凌晨两点执行 - [Install] 部分,定义开机自启动(systemctl enable)和关闭开机自启动(systemctl disable)这个单元时所要执行的命令。WantedBy=multi-user.target意思是,如果执行 systemctl enable mytimer.timer(只要开机,定时器自动生效),那么该定时器归属于multi-user.target。
所谓 Target 指的是一组相关进程,有点像 init 进程模式下面的启动级别。启动某个 Target 的时候,属于这个 Target 的所有进程都会全部启动。
multi-user.target 是一个最常用的 Target,意为多用户模式;当系统以多用户模式启动时,就会一起启动 mytimer.timer。执行systemctl enable mytimer.timer
命令时,就会在 multi-user.target.wants 目录里面创建一个符号链接,指向 mytimer.timer。
常用命令
# 查看所有单元文件
systemctl list-unit-files
# 查看所有 Service/Timer 单元
systemctl list-unit-files --type service/timer
# 单元启动、关闭、重启、杀死、查看状态、开机自动执行、关闭开机自动执行
systemctl start/stop/restart/kill/status/enable/disable [UnitName]
# 查看所有正在运行的定时器。
systemctl list-timers
# 查看整个日志
sudo journalctl
# 查看 mytimer.timer日志
sudo journalctl -u mytimer.timer
# 查看 mytimer.timer 和 mytimer.service日志
sudo journalctl -u mytimer
# 从结尾开始查看最新日志
sudo journalctl -f
# 从结尾开始查看 mytimer.timer日志
journalctl -f -u timer.timer