# 仅执行一次的工作排程

# atd 的启动

atd 并非所有的 distribution 都会预设打开,因此掌握他的启动和关闭等方式

systemctl restart atd		# 重新启动 atd 服务
systemctl enable atd		# 开机自动启动
systemctl status atd		# 查询 atd 状态
1
2
3
[root@study ~]# systemctl status atd
● atd.service - Job spooling tools
   Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
   Active: active (running) since 五 2020-03-06 13:31:07 CST; 25min ago
 Main PID: 1382 (atd)
    Tasks: 1
   CGroup: /system.slice/atd.service
           └─1382 /usr/sbin/atd -f

3月 06 13:31:07 study.centos.mrcode systemd[1]: Started Job spooling tools.
# 上面显示 enabled 和 actice(running) 标识开机启动,和正在运行中
1
2
3
4
5
6
7
8
9
10
11

# at 的运行方式

使用 at 指令产生的工作,会以文本方式写入 /var/spool/at/ 目录内,该工作等待 atd 这个服务的取用与执行

另外为了安全,不要随意吧 at 指令执行权限下发,否则有黑客会使用定时任务来收集数据等情况

可以利用 /etc/at.allow/etc/at.deny 文件来进行 at 的使用限制,加上这两个文件后,at 的工作情况如下:

  1. 先查找 /etc/at.allow 文件,在该文件中的使用者才能使用 at
  2. 如果 /etc/at.allow 不存在,就查找 /etc/at.deny 文件,写在这个 at.deny 中的使用者无法使用 at
  3. 如果两个文件都不存在,则只有 root 可以使用 at 指令

那么两个文件的含义如下:

  • at.allow:严格限制,只有存在该文件中才能使用
  • at.deny:宽松限制,不存在该文件中则可以使用,也就是说,如果该文件是一个空文件,并且没有 at.allow 文件,那么就表示任何人都可以使用 at 指令

# at 语法

at [-mldv] TIME
at -c 工作号码

选项与参数:
	-m:当 at 工作完成后,即使没有输出信息,也以 email 通知使用者该工作已完成
	-l:at -l 相当于 atq,列出目前系统上的所有该用户的 at 排程
	-d:at -d 相当于 atrm,可以取消一个再 at 排程中的工作
	-v:可以使用较明显的时间格式列出 at 排程中的任务栏表
	-c:可以列出后面接的该项工作的实际指令内容
	
	TIME:时间格式,定义什么时候要进行 at 工作的时间,格式有:
		HH:MM	如 4:00,在今日 4 点执行,若该时刻已过,则在明天的 4 点执行
		HH:MM YYYY-MM-DD	如 4:00 2020-03-06 ,就在该时间点执行
		HH:MM[am|pm] [Month] [Date]	如:04:00pm July 30,就在该时刻执行
		HH:MM[am|pm] + number [minutes|hours|days|weeks]
			如:now + 5 minutes、04pm + 3 days
			在某个时间点再 + 几个时间后才执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

可以看到,该指令的复杂的就是时间的指定了

# 范例 1: 5 分钟后,将 /root/.bashrc 寄给 root 自己
[root@study ~]# at now + 5 minutes		# 按回车后,输入要执行的指令
at> /bin/mail -s "testing at job" root < /root/.bashrc		
at> <EOT>		# 需要使用 ctrl + d 结束输入
job 3 at Fri Mar  6 14:22:00 2020
# at 工作已经创建,他的 ID 是 3, 会在 2020-03-06 14:22:00 执行

# 上面使用 at 指令会进入 at shell 环境,让你下达多重指令的运行
1
2
3
4
5
6
7
8
# 范例 2:将上述第 3 项工作内容查询出来
[root@study ~]# at -c 3
#!/bin/sh			# 可以看出来是通过 bash shell 执行的
# atrun uid=0 gid=0
# mail mrcode 0
umask 22
。。。。。
cd /root || {
	 echo 'Execution directory inaccessible' >&2
	 exit 1
}
${SHELL:-/bin/sh} << 'marcinDELIMITER4065fff8'
# 下面这一行就是我们要执行的指令了
/bin/mail -s "testing at job" root < /root/.bashrc

marcinDELIMITER4065fff8

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

上面没有写出来的很多类容,大部分是设置了很多环境变量。

# 范例 3:由于机房预计 2020-03-08 停电,想要在 2020-03-07 23:00 关机
[root@study ~]# at 23:00 2020-03-07
at> /bin/sync
at> /bin/sync
at> /sbin/shutdown -h now
at> <EOT>
job 5 at Sat Mar  7 23:00:00 2020
1
2
3
4
5
6
7

由于指令的下达与 PATH 变量有关,同时与当时的工作目录也有关联(如果涉及到文件),因此使用绝对路径来下达指令,比较不容易出问题,

举例说明:你在 /tmp 下达 at now 然后输入 mail -s "test" root < .bashrc ,那么该文件的指向则是 /tmp/.bashrc因为 at 在运行时,会跑到当时下达 at 指令的那个工作目录

还有一个需要注意的,at 的执行与终端机环境无关,而所有 standard output/standard error output 都会传送到执行者的 mailbox 去,所以在 at 中执行 echo "Hello" ,并不会再终端机上看到该信息。有种方法可以达到显示在终端机上 echo "hello" > /dev/tty1 ,把输出信息定向给了具体的 tty 终端机,前提是你登录了该终端机

还记得 -m 指令吗,如果 at 执行后,没有输出任何信息,则不会发送 email 给执行者的,使用 -m 指定之后,无论有没有输出信息,都会发送

还有 at 是在背景执行的,与 bash 的 nohup(第 16 章)类似,系统会将该项 at 工作独立出你的 bash 环境中,直接交给系统的 atd 程序来接管,因此下达了 at 的工作后,就可以脱机了(你断开登录的 tty 之后,不会让 at 执行工作也失效)。

# at 工作的管理

可以对 at 的查询和移除等工作

atq
atrm (jobnumber)
1
2
# 范例 1:查询目前主机上有多少 at 工作排程
[root@study ~]# atq
# 编号	日期					谁下达的
5	Sat Mar  7 23:00:00 2020 a root

# 这里看不出来内容,还可以利用上面的  at -c 5 来确认
[root@study ~]# at -c 5
${SHELL:-/bin/sh} << 'marcinDELIMITER464e26fe'
/bin/sync
/bin/sync
/sbin/shutdown -h now
# 这里就能看到,是上面关机的任务

# 范例 2: 删除上面的任务
[root@study ~]# atrm 5
[root@study ~]# atq
# 没有信息输出,标识没有任务了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# batch:系统空闲时才进行背景任务

batch 会再 CPU 的工作负载小于 0.8 的时候,才进行 at 中的任务;

工作负载:CPU 在单一时间点所负责的工作数量,不是 CPU 使用率。比如:有一个程序需要一直使用 CPU 运算功能,那么 CPU 的使用率可能达到 100%,但是 CPU 的工作负载则是趋近于 1,如果有两个这样的程序,那么 CPU 使用率还是 100%,但是工作负载就变成了 2,也就是可以看成为多线程,CPU 同时为几个线程提供服务

所以 CPU 工作负载越大,CPU 越忙碌

CentOS 下的 batch 已经不再支持时间参数了,因此 batch 可以拿来作为判断是否要立刻执行背景程序的依据,为了模拟 CPU 较高的工作负载,使用 12 章里面的计算 PI 的脚本,连续执行 4 次这样的程序。

[root@study ~]# echo "scale=100000; 4*a(1)" | bc -lq &
[1] 4677
[root@study ~]# echo "scale=100000; 4*a(1)" | bc -lq &
[2] 4679
[root@study ~]# echo "scale=100000; 4*a(1)" | bc -lq &
[3] 4681
[root@study ~]# echo "scale=100000; 4*a(1)" | bc -lq &
[4] 4683
# uptime 查看负载
[root@study ~]# uptime
 17:04:28 up  3:33,  1 user,  load average: 0.47, 0.11, 0.08
[root@study ~]# uptime
 17:04:33 up  3:33,  1 user,  load average: 0.75, 0.18, 0.10
[root@study ~]# uptime
 17:04:39 up  3:34,  1 user,  load average: 1.33, 0.32, 0.15
# 可以看到,平均负载飙升到 1.33 了

[root@study ~]# batch
at> /usr/bin/updatedb
at> <EOT>
job 6 at Fri Mar  6 17:05:00 2020
[root@study ~]# date;atq
2020年 03月 06日 星期五 17:06:25 CST
6	Fri Mar  6 17:05:00 2020 b root
# 可以看到,时间已经过了,缺没有执行  at 任务

[root@study ~]# 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 &
# 使用 jobs 找出背景功能,再使用 kill 删除掉 4 个背景工作后,等待工作负载的下降
[root@study ~]# kill -9 %1 %2 %3 %4

# 需要等待工作负载降低到 0.8 以下
[root@study ~]# uptime; atq
 17:09:32 up  3:38,  1 user,  load average: 2.29, 2.30, 1.10
6	Fri Mar  6 17:05:00 2020 b root
[root@study ~]# uptime; atq
 17:09:38 up  3:39,  1 user,  load average: 2.10, 2.27, 1.09
6	Fri Mar  6 17:05:00 2020 b root
[root@study ~]# uptime; atq
 17:11:20 up  3:40,  1 user,  load average: 0.52, 1.63, 0.99
6	Fri Mar  6 17:05:00 2020 = root
[root@study ~]# uptime; atq
 17:11:21 up  3:40,  1 user,  load average: 0.52, 1.63, 0.99
# 这里降低到 0.52 还能看到还没有执行,多等待下,就会发现不执行了,
# 这个是因为指令的执行完成也需要一定时间的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

平均工作负载:是统计的 1 分钟内的平均值,当小于 0.8之后的「整分钟时间」,atd 会执行 batch 的工作

整分钟时间:无论 at 还是 crontab,他们最小的时间单位是分钟,所以基本上,他们工作是 每分钟检查一次 ,就是整分(秒为 9 的时候)。另外 batch 也是使用 atq/atrm 来管理的