12、Linux 基础 - 逻辑卷管理器 和 计划任务

逻辑卷管理器

逻辑卷管理器(Logical Volume Manager)

Volume:体积、容量、响度

我们有的时候会将磁盘分区太多,造成浪费;有的时候又会分区太少,不够用。重新分区挂载磁盘又不免有些麻烦,这该如何是好?

我们今天来介绍LVM这个东西。

LVM重点在于【可以弹性地调整文件系统的容量】,而并不在于性能与数据安全上面。

LVM可以整合多个物理分区,让这些分区看起来像一个磁盘一样。而且,未来还可以在这个LVM管理的磁盘当中新增或删除其他的物理分区。

什么是LVM:PV、PE、VG、LV 的意义

意义

LVM,逻辑卷管理器。

LVM的做法是将几个物理的分区(或磁盘)通过软件组合成为一块看起来是独立的大磁盘(VG),然后将这块大磁盘再经过划分成为可使用的分区(LV),最终就能够挂载使用了。但是为什么这样的系统就可以进行文件系统的扩充与缩小呢?其实与一个称为PE的东西有关。下面我们来具体聊聊这些东西。

  • 物理卷(Physical Volume,PV)

我们实际的分区(或Disk)需要调整系统标识符(system ID)成为8e(LVM的标识符),然后再经过pvcreate的命令将它转成LVM最底层的物理卷(PV),之后才能够将这些PV加以利用。调整system ID的方式就是通过gdisk。

  • 卷组(Volume Group,VG)

所谓的LVM大磁盘就是将许多PV整个成这个VG,所以VG就是LVM组合起来的大磁盘,这么想就好了。那么这个大磁盘最大可以达到多少容量呢?这与下面要说明的PE以及LVM的格式版本有关。在默认的情况下,使用32位的Linux系统时,基本上LV最大仅能支持到65534个PE 而已;若使用默认的PE为4MB的情况下,最大容量则仅能达到约 256GB 而已。不过,这个问题在64位的Linux系统上面已经不存在了,LV几乎没有什么限制了。

  • 物理扩展块(Physical Extent,PE)

LVM默认使用4MB的PE数据块,而LVM的LV在32位系统上最多仅能含有65534个PE(lvm1的格式),因此默认的LVM的LV会有4MB*65534/(1024M/G)=256GB。这个PE很有趣,它是整个LVM最小的存储数据单位,也就是说,其实我们的文件数据都是借由写入PE来完成的。简单地说,这个PE就有点像文件系统里面的block大小。所以调整PE会影响到LVM的最大容量。不过,在CentOS 6.x以后,由于直接使用lvm2的各项格式功能,以及系统转为64位,因此这个限制已经不存在了。

  • 逻辑卷(Logical Volume,LV)

最终的VG还会被切成LV,这个LV就是最后可以被格式化使用的类似分区的东西了。那么LV是否可以随意指定大小呢?当然不可以。既然PE是整个LVM的最小存储单位,那么LV的大小就与再次LV以内的PE总数有关。为了方便用户利用LVM来管理其系统,LV的设备文件名通常为【/dev/vgname/lvname】的样式。

此外,我们刚刚提到LVM可以弹性地修改文件系统的容量,那是怎么办到的呢?其实他就是通过【交换PE】来进行数据交换,将原本LV内的PE转移到其他设备中以降低LV的容量,或是将其他设备的PE加到此LV中以加大容量。

因为是逻辑的,不是物理的,所以很方便就能划分LV。

实现思路

 

如此一来,我们就可以利用LV这个玩意儿来进行系统的挂载了。不过,我的数据写入这个LV时,它是怎么写入到磁盘中的呢?根据写入机制,有两种方式:

  • 线性模式(linear):假如我将/dev/sda1、/sda/vdb1这个两个分区加入到VG中,并且整个VG只有一个LV时,那么所谓的线性模式就是当/dev/vda1 的容量用完之后,/dev/vdb1的硬盘才会被使用到。
  • 交错模式(triped):我将一块数据拆成两份,分别写入/dev/vda1与/dev/vdb1(有点像RAID 0),如此一来,一份数据两块硬盘来写入,理论上读写的性能会更好

基本上,LVM最主要的用处是在实现一个可以弹性调整容量的文件系统上,而不是建立一个性能为主的磁盘上。因此,LVM默认的读写模式是线性模式。如果使用triped模式,任何一个分区损坏,所有的数据都会损坏,所以,不适合这种模式。

如果强调性能与备份,直接使用RAID即可,不需要用到LVM。

LVM实现流程

LVM必须要有内核支持且需要安装lvm2这个软件。

我们计划的LVM实现流程:

1、 使用4个磁盘分区,每个分区的容量均为1GB左右,且systemID需要为8e;
2、 全部的分区整合成为一个VG,VG名称设置为vbirdvg,且PE的大小为16MB;
3、 建立一个名为vbirdlv的LV,容量为2GB;
4、 最终这个LV格式化为xfs的文件系统,且挂载在/src/lvm;

0x00.Disk阶段

磁盘分区

我们通过以前学习的gdisk命令来完成下面的范例

关于分区具体步骤请参考Linux的基本学习(四)——磁盘与文件系统管理

分区完成后按w写入并退出,同时使用命令partprobe更新Linux磁盘信息。

使用命令查看/dev/sda信息

gdisk -l /dev/sda

 

上面的/dev/sda{4,5,6,7} 这4个分区就是我们的物理分区,也就是下面会实际用到的信息,至于/dev/sda8 先留着不用。

注意看,那个8E的出现会导致system变成【Linux LVM】。其实不设置成为8e也没有关系,不过某些LVM的检测命令可能会检测不出该分区。接下来就是一个个地处理各流程。

0x01.PV阶段

分区进化成物理卷

要建立PV其实很简单,只要使用 pvcreate 即可。

我们来谈一谈与PV有关的命令:

  • pvcreate:将物理分区建立成为PV
  • pvscan:查找目前系统里面任何具有PV的磁盘
  • pvdisplay:显示出目前系统上面的PV状态
  • pvremove:将PV属性删除,让该分区不具有PV属性。

那就直接来看一看:

 

这样就建立起来了PV。

0x02.VG阶段

物理卷拼合成卷组

建立VG相关的命令:

  • vgcreate:主要建立VG的命令,它的参数比较多,一会介绍
  • vgscan:查找系统上面是否有VG存在
  • vgdisplay:显示目前系统上面的VG状态
  • vgextend:在VG内增加额外的PV
  • vgreduce:在VG内删除PV
  • vgchange:设置VG是否启动(active)
  • vgremove:删除一个VG

与PV不同的是,VG的名称是自定义的。我们知道PV的名称其实就是分区的设备文件名,但是这个VG名称则可以随便你自己取。在下面的例子中,我将和鸟哥一样,将VG的名称取名为vbirdvg。建立这个VG的流程是这样的:

vgcreate [-s N[mgt]] VG 名称 PV名称
-s:后面接PE的大小(size)单位可以是m、g、t(大小写均可)

 

这样我们就建立了一个VG了。假设我们要增加这个VG的容量,我们可以利用没有使用的PV——/dev/sda7

 

我们于是就多了一个设备,接下来我们为这个vbirdvg进行分区,通过LV来完成

0x03.LV阶段

创造出VG这个大磁盘后,再来就是要建立分区,这个分区就是所谓的LV。假设我要将刚刚那个vbirdvg磁盘,划分为vbirdlv,整个VG的容量都被分配到vbirdlv里面中。

先来看看可以使用的命令:

  • lvcreate:建立lv
  • lvscan:查询系统上面的LV
  • lvdisplay:显示系统上面的LV状态
  • lvextend:在LV里面增加容量
  • lvreduce:在LV里面减少容量
  • lvremove:删除一个LV
  • lvresize:对LV进行容量大小的调整
lvcreate [-L N[mgt]] [-n LV名称] VG名称
lvcreate [-l  N]  [-n   LV名称]  VG名称
-L:后面接容量、容量的单位可以是M、G、T等,
    要注意的是,最小单位为PE,因此这个数量
    必须是PE的倍数,若不相符,系统会自行计
    算最相近的容量。
-l:后面可以接PE的个数,而不是数量
-n:后面接的就是LV的名称

 

如此一来,整个LV分区也准备好。接下来,就是针对整个LV来处理。要特别注意的是,VG的名称为vbirdvg,但是LV的名称必须使用全名,即/dev/vbirdvg/vbirdlv才对,后续的处理都是这样的。

0x04.文件系统阶段

格式化建立文件系统

 

我这里以前有过文件系统,所以这里使用了-f参数强制创建文件系统

结束,我们创建好了一个文件系统了。

放大LV容量

为什么我们使用LVM,不用原先简单的分区格式化,因为LVM可以弹性调整磁盘容量。

好,那么放大LV的容量时,我们该怎么做?

1、 VG阶段需要有剩余的容量,放大文件系统,所以需要放大LV,所以需要VG中有足够的剩余空间;
2、 LV阶段产生更多的可用容量:如果VG的剩余容量足够,此时就可以利用lvresize这个命令来将剩余容量加入到所需要增加的LV设备内;
3、 文件系统阶段的放大:我们的Linux实际使用的既不是LV,而是LV这个设备内的文件系统,所以一切最终还是要以文件系统为依存另外提一下,目前xfs文件系统并不支持文件系统容量的缩小,xfs放大文件系统只需要通过简单的xfs_growfs命令即可;

通过以前的文章我们知道,整个文件系统在最初格式化的时候就建立inode、区块、超级区块等信息,修改这些信息是很难的。不过因为文件系统格式化的时候创建的是多个区块群组,所以我们可以通过在文件系统中增加区块群组的方式来增减文件系统的量,而增减区块群组就是利用xfs_growfs。所以最后一步是针对文件系统来处理的,前面几步是针对LVM的实际容量大小的。

严格的来说,放大文件系统并不是没有进行【格式化】。在放大文件系统时,格式化的位置在于该磁盘设备后来新增的部分,磁盘设备的前面已经存在的文件系统则没有变化。而新增的格式化后的数据,只是再反馈回原本的超级区块而已。

忘了什么是超级区块了?简单来说就是 记录该文件系统的inode数和block数的总指挥部,一个文件系统或一个区块群组只能有一个超级区块,如果是区块群组中,其他组的超级区块都是对第一个超级区块的备份。

我们来继续做实验,再刚才的/srv/lvm 再增加 500MB 的容量。

 

最终的结果,LV放大到了2.5GB,而文件系统的大小却没有增加。而且,我们的LVM可以在线直接处理,不需要特别给它 umount ,真是人性化,接下来我们来让文件系统的容量也扩展过去。

 

最后注意,xfs文件系统目前只能放大不能缩小,如果你想要有放大和缩小的本事,请重新使用ext4文件系统。

使用LVM thin Volume 让 LVM 动态自动调整磁盘使用率

LVMthin Volume, 他的概念是:先建立一个可以实用实取、用多少容量才分配实际写入多少容量的磁盘容量存储池(thin pool),然后再由这个thin pool去产生一个【指定要固定容量大小的LV设备】,虽然你会看到【声明上,他的容量可能有 10GB ,但实际上,该设备用到多少容量时,才会从 thin pool去实际获得所需要的容量】

步骤如下:

1、 由vbirdvg的剩余容量取出1GB来做出一个名为vbirdtpool的thinpoolLV设备,这就是所谓的磁盘容量存储池(thinpool);
2、 由vbirdvg内的vbirdtpool产生一个名为vbirdthin1的10GBLV设备;
3、 将此设备实际格式化为xfs文件系统,并且挂在于/src/thin目录内;

实验:

 

 

开始创立这个vbirdthin1这个10G的设备,注意,必须使用–thin与vbirdtpool连接。

 

上面就是“用多少算多少”的 存储池技术,没错,你一定发现了,他其实具有骗人吓人的功能,哈哈。

一定要记住,真正可用的容量是实际的磁盘存储池内的容量,如果突破该容量,这个磁盘容量存储池可是会损毁而让数据破坏的,切记。

LVM的LV磁盘快照

LVM除了弹性扩容、虚拟存储空间吓吓人,还有什么功能呢?

其实他还有一个很重要的能力,就是LV磁盘的快照(snapshot)。

这个快照的效果我相信玩过虚拟机的朋友应该都知道,我就不详述了,我们重点来说说LVM中快照的原理:

 

左图为最初创建的快照的情况,LVM 会预留一个区域 (比如左图的左侧三个 PE 区块) 作为数据存放处。 此时快照区域内并没有任何数据,而快照区域与系统区共享所有的 PE 数据, 因此你会看到快照的内容与系统区中的内容是一模一样的。 等到系统运行一阵子后,假设 A 区域的数据被更新了(上面右图所示),则更新前系统会将该区域的数据移动到快照区中, 所以在右图的快照区中被占用了一块 PE 成为 A,而原来A的位置更新成了newA,而其他 B 到 I 的区块则还是与文件系统共享!

LVM管理器的快照技术是非常棒的【备份工具】,它非常灵活地是实现了选择性备份,使得快照所占用的容量非常小。

创建快照

下面我们针对传统LV磁盘创建快照,流程:

  • 预计被拿来备份的原始LV为 /dev/vbirdvg/vbirdlv 这个东西
  • 使用传统的方式创建快照,原始盘为 /dev/vbirdvg/vbirdlv,快照名为 vbirdsnap1,容量为vbirdvg的所有剩余容量。

 

查看命令发现卷组只剩余26个物理扩展块(PE)了(截图晚了,图中已经用掉那26个了)

 

这个/dev/vbirdvg/vbirdsnap1快照区就建立起来了,而且它的VG量竟然与原本的/dev/vbirdvg/vbirdlv相同,也就是说,如果你挂载这个设备,那么看到的数据和/dev/vbirdvg/vbirdlv相同(因为这两个设备都指向相同内存数据,只不过快照设备(snap)还可以保存数据变更而已)

恢复系统

要注意,你要恢复的数据不能够高于快照区所能负载的实际容量。由于原始数据会被迁移到快照区,如果你的快照区不够大,若原始数据被修改的实际数据量比快照区大,那么快照区当然容纳不了,这时快照的功能就会失效。

 

因为xfs不允许相同UUID文件系统的挂载,而LV和它的快照是同一个数据,所以我们要加上nouuid,让系统忽略UUID

 

这一步是利用快照区将原本的文件系统备份,为什么要备份呢?为什么不可以直接格式化lv,然后将snap1直接复制给lv呢?要知道vbirdsnap1其实是vbirdlv的快照,因此如果你格式化整个vbirdlv时,原本的文件系统所有数据会被迁移到vbirdsnap1,那如果vbirdsnap1的空间不够大(一般也确实不够大),这个snap1就废了,就不能用作复原lv的数据了,所以我们需要备份一份。

接下来我们来还原:

 

使用df -Th /dev/vbirdlv检查,我们会发现这个lv又恢复了51M的Used。

LVM相关命令集合与LVM的关闭

我们将上面的命令整理一下,供您参考

任务 PV 阶段 VG 阶段 LV 阶段
扫描(scan) pvscan vgscan lvscan
创建(create) pvcreate vgcreate lvcreate
列出(display) pvdisplay vgdisplay lvdisplay
添加(extend)   vgextend lvextend (lvresize)
减少(reduce)   vgreduce lvreduce (lvresize)
删除(remove) pvremove vgremove lvremove
改变容量(resize)     lvresize
改变属性(attribute) pvchange vgchange lvchange

至于文件系统阶段,还需要以xfs_growfs来自定义文件系统实际的大小才行(xfs系统只能增不能减)。

LVM虽然可以很好弹性地管理你的磁盘容量,但是要注意,如果你想使用LVM来管理磁盘,那么在安装的时候就要做好LVM的规划。

会玩LVM还不行,你要必须学会删除系统内的LVM,因为你的物理分区已经被LVM使用,如果没有关闭LVM就直接将那些分区删除或转为其他用途的话,系统是会发生很大问题的。删除流程:

1、 先卸载系统上面的LVM文件系统(包括快照与所有LV);
2、 使用lvremove删除LV;
3、 使用vgchange-anVGname让VGname这个VG不具有Active标志;
4、 使用vgremove删除VG;
5、 使用pvremove删除PV;
6、 最后,使用fdisk修改ID回来;

 

因为我的Linux重启过,所以没有srv/thin、srv/snapshot1这次启动后没有被挂载,所以我只卸载了/src/lvm

然后利用gdisk修改磁盘ID为83即可(gdisk /dev/sda =》t=》8300(或者回车采用默认))。


计划任务

什么是计划任务

Linux计划任务的种类

计划任务有两种:

1、 例行性:就是每隔一定的周期就要来办的事项;
2、 突发性:就是这次做完以后就没有的那一种;

在Linux下面怎么实现这两个功能?那就得使用at与crontab这两个好东西:

  • at:at是个可以处理仅执行一次就结束的命令,不过要执行at时,必须要有atd(后面会讲)这个服务的支持才行。CentOS是默认启动的。(突发性)
  • crontab:crontab这个命令所设置的任务将会循环地一直执行下去,可循环的时间为分钟、小时、每周、每月或每年等。crontab除了可以使用命令执行外,亦可编辑 /etc/crontab 来支持,至于让crontab 可以生效的服务则是 crond。(例行性)

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

基本上Liux系统常见的例行性任务有:

  • 执行日志文件的论循(logrotate)

Linux会主动将系统所发生的各种信息记录下来,这就是日志文件。由于系统一直记录登录信息,所以日志文件将会越来越大。我们知道大型文件不但占用容量还会影响读写性能,因此适时地将日志文件数据挪一挪,让旧的数据与新的数据分别存放,则可以更有效地记录登录信息。这就是logrotate的任务,也是系统必要的执行任务。

  • 日志文件分析logwatch的任务

如果系统发生了软件问题、硬件错误、信息安全等问题等,绝大部分的错误都会被记录到日志文件中,因此系统管理员的重要任务之一就是分析日志文件。但你不可能手动通过vim等软件去查看日志文件,因为数据太复杂了。我们的CentOS提供了一个程序【logwatch】来主动分析登录信息,所以你会发现,你的root老是会收到标题为logwatch的邮件,那是正常的。

  • 建立locate的数据库

在之前我们聊过locate这个命令,我们知道该命令通过已经存在的文件名数据库来执行文件名的查询。我们的文件名数据库放置在/var/lib/mlocate/中,系统会主动执行 updatedb 来更新这个数据库。

  • manpage查询数据库的建立

与locate类似,可提供快速查询的 manpagedb 也是那个数据库,但如果要使用 manpage数据库,就要执行mandb才能够建立好,而这个manpage数据库也是通过操作系统的计划任务来自动执行的。

  • RPM软件日志文件的建立

RPM是一种软件管理的机制。由于系统可能经常变更软件,包括软件的全新安装、非经常性更新等,都会造成软件安装文件名的差异。为了方便追踪调查,系统会帮助我们将文件名做个排序的记录。

  • 删除缓存

某些软件在运行过程中会产生一些缓存,但是当这个软件关闭时,这些缓存可能并不会主动地被清楚。有的缓存没有什么用,系统通过 tmpwatch 的命令来删除这些缓存。

  • 与网路服务相关的分析操作

如果你安装了类似网站服务器的软件(比如apache),那么你的Linux系统通常就会主动地分析该软件的日志文件。同时某些凭证与认证的网络信息是否过期的问题,我们的Linux也会帮助我们很好的检查。

仅执行一次的计划任务

首先,我们先来聊一下突发性计划任务——学习at命令的运行

atd的启动与at运行的方式

要使用单一计划任务时,我们的Linux系统上面必须要有负责这类计划任务的服务,那就是 atd这个服务。不过并非所有的Linux发行版都默认启动,所以,某些时刻我们必须要手动将它启动才行。

启动流程:

 

atd服务就启动成功!

既然是计划任务,那么应该会有产生任务的方式,并且将这些任务排进计划表中。OK!那么产生任务的方式是怎么执行的呢?事实上,我们使用at这个命令来产生所要运行的任务,并将这个任务以文本文件的形式写入/var/spool/at/目录内,该任务便能等待atd这个服务的使用与执行了,就这么简单。

不过,并不是所有人都可以执行at任务。这是为了避免黑客程序利用计划任务来执行或搜集系统信息,并定时地返回给黑客团体,所以,除非是你认可的账号,否则不要让他们使用at目录,那怎么实现对at的管控呢?

我们可以利用/etc/at.allow与/etc/at.deny 这两个文件来实现对at的使用限制。加上这两个文件之后,at的工作情况其实是这样的:

1、 先找到/etc/at.allow这个文件,写在这个文件中的用户才能使用at,没有在这个文件中的用户则不能使用;
2、 如果/etc/at.allow不存在,就查找/etc/at.deny这个文件,写在这个at.deny中的用户则不能使用at,而没有写在这个at.deny中的用户,则可以使用at;
3、 如果这两个文件都不存在,那么只有root可以使用这个命令;

如果at,allow和at.deny都有这个用户,那么他还是可以使用at的,因为系统会优先读取at.allow来查看

deny:否认

通过这个说明,我们知道/etc/at.allow是管理较为严格的方式,而/etc/at.deny则较为松散(因为账号没有在文件中,就可以执行at了)。在一般的Linux发行版中,由于假设系统上的所有用户都是可信任的,因此系统通常会保留一个空的/etc/at.deny文件,允许所有人使用at命令(您可以自行检查一下该文件)。

另外,编写这个文件的时候,一个用户写一行。

实际运行单一计划任务

单一计划任务的执行使用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        ex> 04:00
   在今日的 HH:MM 时刻执行,如果该时刻已过,那么就是明天
   HH:MM YYYY-MM-DD          ex> 04:00 2018-5-19
   强制规定在某年某月的某一天特殊时刻执行该任务
   HH:MM[am|pm] [Mouth] [Date]     ex> 04pm July 30
   强制在某年某月某日的某一时刻执行
   HH:MM[am|pm] + number [minutes|hours|days|weeks]
   ex> now + 5 minutes              ex> 04pm + 3 days
   就是说,某个时间点再加几个小时后执行   

老实说,执行at命令最重要的地方在于指定【时间】。下面是示例:

 

 

事实上,当我们使用at时会进入一个at shell的环境来让用户执行用户命令,

此时,建议你最好使用绝对路径来执行你的命令,避免出现问题。

由于命令的执行与path变量有关,同时与当前的工作目录也有关联(如果牵扯到文件的话),因此使用绝对路径来执行命令,会是一劳永逸的方法。at在运行时,会跑到当时执行at命令的那个工作目录。

有些朋友可能希望在某某时刻,在终端输出Hello的字样,然后在at中执行“echo “Hello” ”,这样其实不会显示的。这是因为at的执行与终端环境无关,而所有标准输出/标准错误输出都会发送到执行者的mailbox中。那该怎么办?可以通过终端的设备来处理。假设你在tty2登录,那么可以使用:

echo "Hello" > /dev/tty2

at还有另外一个很棒的优点,那就是“后台执行”的功能。

什么是后台执行?示例:

  • 脱机继续执行的任务
  • 突发情况导致必须要执行的任务

由于at计划任务的使用,系统会将该项at任务独立出你的bash环境,直接交给系统的atd来接管。因此,在你执行了at的任务之后就可以立刻脱机了,剩下的工作就完全交给Linux管理。

at任务的管理

设定好at之后,我们可以利用atq和atm将它删除

 

atq 相当于 at -l
atrm 相当于 at -d
如此一来,你可以利用 atq 来查询,利用 atrm 来删除错误的命令,利用 at 来直接执行单一计划任务,很简单。

batch:系统有空时才执行的任务

batch:批

其实batch是利用at来执行命令的,只是加入了一些控制参数而已,这个batch神奇的地方在于:它是在CPU的任务负载小于0.8的时候,才执行你的任务。

任务负载0.8:CPU在单一时间点所负责的任务数量,而不是CPU的使用率。例如,如果我有一个程序需要一直使用CPU的运算功能,那么此时CPU的使用率可能达到100%,但是CPU的任务负载趋近于1,因为CPU仅负责执行一个任务嘛。如果同时执行两个这样的任务,CPU的使用率还是100%,但是任务负载变成了2.

所以也就是说,CPU的任务负载大,代表CPU必须要在不同的任务之间执行频繁的任务切换。因为一直切换任务,所以会导致系统忙碌,还要额外执行at,不太合理,所以才会有batch。

循环执行的计划任务

相对于at是仅执行一次的任务,循环执行的计划任务是由cron(crond)这个系统服务来控制的。由于Linux系统上面原本就有非常多的例行性计划任务,因此这个系统服务不同于at需要atd服务才能运行,这个cron是默认启动的。另外,Linux也提供给用户控制计划任务的命令crontab来让用户自己设定计划任务。

用户的设置

用户想要建立循环型计划任务时,使用crontab这个命令。不过,为了避免安全性的问题,与at同样的,我们可以限制使用crontab的用户账号。可以使用的配置文件有:

  • /etc/cron.allow:不在这个名单中的用户则不可以使用crontab
  • /etc/cron.deny:黑名单,未记在这个文件中的用户,就可以使用crontab。

与at非常相似,/etc/cron.allow比/etc/cron.deny要优先。而判断上面,这两个文件只选择一个来限制而已。因此,建议你只保留一个即可,免得影响自己在设置上面的判断。一般来说,系统默认保留/etc/crom.deny,你可以将不想让他执行crontab的那个用户写入/etc/cron.deny当中,一个账号一行。

当用户使用crontab这个命令来建立任务之后,该项任务就会被记录到/var/spool/cron/中,而且是以账号来作为判断依据的。另外注意,请不要使用vi来直接编辑该文件,因为可能由于输入语法错误,会导致无法执行cron。

cron执行的每一项任务都会被记录到/var/log/cron这个日志文件中。

crontab [-u username] [-l|-e|-r]
-u:只有root才能执行这个任务,亦即帮其他使用者建立/删除crontab计划任务
-e:编辑 crontab 的任务内容
-l:查看 crontab 的任务内容
-r:删除所有的crontab 的任务内容,若仅要删除一项,请用-e去编辑

默认情况下,任何用户只要不被列入/etc/cron.deny当中,就能直接执行 crontab -e 去编辑自己的例行性命令。会进入vi编辑界面,编辑完后直接wq退出即可。

编辑任务中,每行(每个任务)的格式都是具有六个字段:

minute hour day month week command

其中:

  • minute: 表示分钟,可以是从0到59之间的任何整数。
  • hour:表示小时,可以是从0到23之间的任何整数。
  • day:表示日期,可以是从1到31之间的任何整数。
  • month:表示月份,可以是从1到12之间的任何整数。
  • week:表示星期几,可以是从0到7之间的任何整数,这里的0或7代表星期日。
  • command:要执行的命令,可以是系统命令,也可以是自己编写的脚本文件。

另外,还有一些比较特殊的字符:

 特殊字符  代表含义
 *(星号)  代表任何时刻都接受的意思,*代表的是任何时候
 ,(逗号)  代表分隔字段的意思

 

例如: 15,30,45 * * * * command 代表的是每月每日每小时的15分,30分,45分的时候执行命令

-(减号)  代表一段时间范围内

 

例如: 10 7-10 * * * command 代表的是每月每日的7点到10点的10分整时执行命令

 /n  那个 n 代表数字,也就是每隔 n 单位间隔的意思

 

例如 */5 * * * * command 代表的是每月每日每时每个5分钟执行一次,这种写法等价于 0-59/5 * * * * command

那个crontab每个人都只有一个文件存在,就是在/var/spool/cron里面,建议您:命令执行时,最好使用绝对路径,这样比较不会找不到执行文件。

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

这个crontab -e是针对用户的cron来设计的,如果要执行【系统的例行性任务】时,该怎么办?是否还需要用crontab -e来管理你的计划任务?当然不需要,你只需要编辑/etc/crontab这个文件就可以。有一点需要注意,那就是crontab -e这个crontab其实是/usr/bin/crontab这个执行文件,但是/etc/crontab 可是一个纯文本文件,你可以用root的身份编辑一下这个文件。

基本上,cron这个服务的最低检测限制是【分钟】,所以【cron每分钟会去读取一次/etc/crontab与/var/spool/cron里面的数据内容】。

有的UNIX系统中,由于craontab是读取到内存中的,因此修改完/etc/crontab后并不会立即生效,这个时候请重新启动服务:systemctl restart crond

我们来看一下这个/etc/crontab的内容:

 

这个文件与我们刚刚执行的crontab -e的内容几乎一模一样,只是有几个地方不太相同:

  • **MAILTO=root:**这个选项是说,当/etc/crontab这个文件中的例行性工作的命令发生错误时,或是该任务的执行结果有标准输出/标准错误时,会将错误信息或是屏幕显示的信息传给谁?默认当然是由系统直接发一封email给root。不过,由于root无法在客户端中以POP3之类的协议收信,因此,鸟哥通常都将这个email改成自己的账号,好让我随时了解系统的情况。
  • **PATH=…:**这里就是输入执行文件的查找路径,使用默认的路径设置就已经足够了
  • **【分 时 日 月 周 身份 命令】7个字段的设置:**与crontab -e相比,前面同样是分、时、日、月、周五个字段,但是在5个字段后面接的并不是命令,而是一个新的字段,那就是【执行后面那串命令的用户身份】,这与用户的crontab -e是不同。系统默认的计划任务是以root的身份来执行的。

一般来说,crond默认有3个地方会执行脚本配置文件,他们分别是:

1、 /etc/crontab;
2、 /etc/cron.d*;
3、 /var/spool/cron/*;

这三个地方,跟系统的运行有关系的两个配置文件是/etc/crontab文件以及/etc/cron.d* 目录内的文件,另外一个是跟用户自己的任务有关系的配置文件,就是放在/var/spool/cron/里面的文件。

我们来看看/etc/cron.d*里面的东西:

 

如果你想要自己开发新的软件,该软件拥有自己的crontab定时命令时,就可以将【分、时、日、月、周、身份、命令】的配置文件放置到/etc/cron.d/目录下。在此目录下的文件是【crontab的配置文件脚本】。

另外,我们看到下面的命令,每个整点的一分会执行【run-parts /etc/cron.hourly】这个命令,run-parts是一个shell脚本,run-parts脚本会在大约5分钟内随机选一个时间来执行/etc/cron.hourly 目录内的所有执行文件。因此,放在/etc/cron.hourly的文件,必须是能够被直接执行的命令脚本,而不是分、时、日、月、周的设置。

也就是说,除了自己指定分、时、日、月、周加上命令路径的crond配置文件之外,你也可以直接将命令放到(或链接到)/etc/cron.hourly 目录下,这样该命令就会被crond在每小时的第1分开始后的5分钟内,随机选一个时间点来执行,你无须手动去指定分、时、日、月、周。

最后我们总结以下:

  • 个人化的操作使用crontab -e:如果你是根据个人需求来建立例行性计划任务,建议直接使用crontab -e来建立你的计划任务最佳,这样也能保证你的任务不会被大家都看到(etc/crontab是大家都能读取的权限)。
  • 系统维护管理使用【vim /etc/crontab】:如果你的例行性任务是系统的重要任务,为了让自己管理方便,同时容易追踪,建议直接写入/etc/crontab
  • 自己开发软件使用【vim /etc/cron.d/newfile】:如果你是想自己开发软件,那当然使用全新的配置文件最好,并且放置于/etc/cron.d

一些注意事项

有的时候,我们以系统的cron来执行任务的建立时,要注意一些使用方面的特性。举例来说,如果我们有四个任务都是五分钟执行一次的,那么是否这四个操作全部都在同一个时间点执行呢?如果同时执行,系统可能会忙的要死。此时好好地分配一些运行时间就OK。所以,要注意以下内容:

  • 资源分配不均的问题

大量使用crontab的时候,总是会有问题发生。最严重的问题就是【系统资源分配不均】。

所以对于一些计划任务,可以将时间“交错”一分钟左右来减少系统负载量。

  • 取消不要的输出选项

另外一个困扰发生在【当有执行成果或是执行的选项中有输出的数据时,该数据将会mail给MAILTO设置的账号】。好,那么当有一个任务一直出错(例如DNS的检测系统当中,若DNS上层主机挂掉,那么你就会一直受到错误信息),你就会一直受到骚扰信息,心烦。解决方法:直接用数据流重定向将结果输出到/dev/null这个垃圾桶当中。

  • 安全的检验

很多时候,木马都是以计划任务命令的方式植入的,所以可以借由检查/var/log/cron的内容来观察是否有【非您设置的cron被执行了】。

  • 周与日月不可同时并存

另一个需要注意的地方在于:你可以分别以周或是日月为单位作为循环,但你不可使用几月几号且为星期几的模式任务,例如:

30 12 11 9 5 root echo "just test"

本来你以为九月十一日且为星期五来执行任务,但是系统可能会判定每个星期五做一次,或每年的9月11号执行。

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

在关机的时候,计划任务是不会被执行的。但是有的情况下我们需要一些“有记性”的计划任务。我们来介绍anacron。

anacron

anacron并不是用来替换crontab,他主要用于处理非24小时运行的Linux系统所执行的crontab,以及因为某些原因导致的超过时间而没有被执行的任务。

其实anacron也是每小时被crond执行一次,然后anacron再去检测相关的计划任务有没有被执行,如果有超过期限的任务在,就去执行该任务,执行完毕或无须执行任何任务时,anacron就停止。

由于anacron默认会以一天、七天、一个月为期去检测系统未执行的crontab任务,因此对于某些特殊的使用环境会非常有帮助。

它的原理是通过读取时间记录文件。anacron会去分析现在的时间和时间记录文件所记载的上次执行anacron的时间,两者比较后若发现有差异,那就是在某些时刻没有执行crontab,此时anacron就会开始执行未执行的crontab任务了,这就是它判断你的系统什么时候关机的道理。

anacron与/etc/anacrontab

anacron其实是一个程序并非一个服务,这个程序在CentOS当中已经进入crontab的任务列表,同时anacron会每小时被主动执行一次(每小时主动执行一次,是的,anacron的配置文件就是放在/etc/cron.hourly目录下)。

[luoluo@study ~]$ 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
online=1
for psupply in AC ADP0 ; do
    sysfile="/sys/class/power_supply/$psupply/online"

    if [ -f $sysfile ] ; then
        if [ cat $sysfile 2>`/dev/nullx = 1x ]; then
            online=1
            break
        else
            online=0
        fi
    fi
done
if [ $online = 0 ]; then
    exit 0
fi
/usr/sbin/anacron -s

上面的shell脚本我们可以看出来它其实也是执行anacron -s这个命令,我们来谈谈这个程序。

基本上,anacron的语法如下:

anacron [-sfn] [job]..
anacron -u [job]..
-s:开始连续地执行各项任务(job),会根据时间戳考虑执行
-f:强制执行,而不去判断时间记录文件的时间戳
-n:立即执行未执行的任务,而不延迟等待时间
-u:仅更新时间记录文件的时间戳,不执行任务
job:由/etc/anacrontab定义的各项任务的名称

anacron其实每小时都会被抓出来执行一次,但是担心anacron误判时间参数,因此/etc/cron.hourly/里面的anacron才会在文件名之前加个0(0anacron),让anacron最先执行,就是为了让时间戳先更新,以避免anacron误判crontab尚未执行的任务。

接下来我们来看一下anacron的配置文件/etc/anacrontab的内容:

 

我们拿/etc/cron.daily/那一行的设置来说明好了,那四个字段的意义分别是:

  • 天数:anacron执行当前与时间戳(/var/spool/anacron/内的时间记录文件)相差的天数,若超过此天数,就准备开始执行;若没有超过此天数,则不予执行后面的命令。
  • 延迟时间:若确定超过天数导致要执行计划任务了,那么请延迟执行的时间,因为担心立即启动会有其他冲突的问题。
  • 工作名称定义:这个没啥意义,只是在/var/log/cron里面记录该项任务的名称而已,通常与后续的目录资源名称相同即可。
  • 实际要执行的字符串:通过run-parts执行的任务(与0hourly很像)

根据上面的配置文件内容,我们大概知道 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 程序结束。

现在你知道为什么隔了一阵子才将CentOS启动,启动后约1小时系统会有一小段时间的忙碌,而且硬盘会跑个不停,那就是因为anacron正在执行过去/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthly/里面未执行的各项计划任务。

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/;
内的执行文件,以进行固定周期需要执行的指令。