树莓派开始,玩转Linux
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第10章 规划小能手

树莓派是一款低成本的电脑,因此它常充当小型的服务器,定期执行某些任务。笔者平时就会在局域网下接入树莓派,做一些数据备份和上传的工作。这时任务内容和执行时间已经明确。我们想把任务内容和执行时间预先写入树莓派中,让树莓派自动执行。这样用户就不用手动操作树莓派了。为了满足这一需求,Linux系统提供了经典的cron工具。

10.1 用cron规划任务

cron是Linux系统下常用的任务规划软件,可以在cron中要求系统在特定的时间执行特定的任务。cron在系统中有一个运行着的守护进程。当系统时间符合某一条规划记录时,守护进程就会启动相应的任务。在树莓派命令行中运行下面的命令,就可以找到cron的守护进程:

$ps aux | grep cron

这一行实际上调用了两个命令:用于查询进程的ps命令和用于文本搜索的grep命令。其中的|是管道符号,它像管道一样,把ps命令的输出传给grep命令作为输入。

结果如下:

    root      424  0.0  0.2  5072  2384 ?       Ss  14:40  0:00 /usr/sbin/cron -f
    pi       6938  0.0  0.2  4280  2008 pts/1   S+  17:42  0:00 grep --color=auto
cron

记录中的第一条就是cron的进程。

如果想要规划任务,那么可以用下面的命令来编辑规划记录:

    $crontab -e

在规划记录中,每一行为一条记录,以#开始的是注释。每一行记录又分为6列,用空格分隔,分别表示分钟(m,0~59)、小时(h,0~23)、一个月中的哪一天(dom,1~31)、月(mon,1~12)、一个星期中的哪一天(dow,0~6),以及要执行的命令。在填写规划时间时,除了用数字,还可以用*表示所有:

    # m h  dom mon dow  command
     30 5  10  3  *  touch /tmp/test.log

上面表示每年3月10日5点30分,执行touch命令。

    # m h  dom mon dow  command
     10 18  *  *  *  echo "Hello World" > /home/pi/log

上面表示每天的18点10分执行echo命令。

在同一列中,还可以规划多个时间点,例如:

    # m h  dom mon dow  command
     10 2-4  *  *  *  echo "Hello World" > /home/pi/log

每天2:10、3:10和4:10执行。也就是说,“2-4”表示了从2到4的范围。

    # m h  dom mon dow  command
     30 1,5  *  *  *  echo "Hello World" > /home/pi/log

每天1:30和5:30执行。也就是说,“1,5”表示了1和5两个时间点。

规划记录crontab保存后,cron就将按照规划,在对应的时间执行对应的命令。每个用户有一个自己的crontab,当cron要执行规划时,也会以相应的用户身份来执行。这里是以pi用户修改保存的crontab, cron就会以pi的身份来运行各个命令。如果想修改其他用户的crontab,那么可以用-u关键字:

    $sudo crontab -e -u root

10.2 用cron开机启动

cron除了做时间规划,还可以用于开机启动。在crontab中添加下面一行记录,就可以方便地实现开机启动:

    @reboot touch /home/pi/reboot.log

10.3 用/etc/init.d实现开机启动

树莓派的/etc/init.d文件夹下有很多脚本,比如cron。cron脚本把cron这个守护进程包装成了一个服务,定义了它在启动、重启和终止时的具体行为。这样,用户在启用相应服务时,就不用进行太复杂的设置。当服务终止时,操作系统也能根据脚本的定义,自动回收相关资源。用户还能把重要的服务设置成开机启动,省去了手动开启的麻烦。因此,可以在/etc/init.d中看到很多默默工作的服务,如ssh、bluetooth、rsync等。

服务脚本遵循特定的格式,如下面的/etc/init.d/test脚本:

    #! /bin/sh
    # Start/stop the test daemon.
    #
    ### BEGIN INIT INFO
    # Provides:        test
    # Required-Start:   $remote_fs $syslog $time
    # Required-Stop:    $remote_fs $syslog $time
    # Default-Start:    2 3 4 5
    # Default-Stop:     0 1 6
    # Short-Description: test
    # Description:      test
    ### END INIT INFO

    do_start() {
        echo "start"
    }

    do_stop() {
        echo "stop"
    }

    do_restart() {
        echo "restart"
    }

    do_status() {
        echo "status"
    }

    do_fallback() {
        echo "fallback"
    }

    case "$1" in
    start)  do_start
          ;;
    stop)  do_stop
          ;;
    restart) do_restart
          ;;
    status) do_status
          ;;
    *)     do_fallback
          ;;
    esac
    exit 0

脚本的一开始有头部信息。头部信息中除了基本的介绍,还有其他信息。Required-Start说明了该test应用启动前,系统必须启动的其他应用。Required-Stop列出的应用必须在test应用结束后结束。Default-Start和Default-Stop中说明了默认运行级别。Linux系统可以在不同运行模式下工作,如单用户模式、多用户模式,每种模式就称为一个运行级别。Linux系统中运行级别的意义如下:

0停机,关机。

1单用户,无网络连接,不运行守护进程,不允许非超级用户登录。

2多用户,无网络连接,不运行守护进程。

3多用户,正常启动系统。

4用户自定义。

5多用户,带图形界面。

6重启。

test脚本中,默认支持的运行级别是2、3、4、5。

在脚本的主体程序中包含了一个case分支结构,说明了应用在进入启动(start)、停止(stop)、重启(restart)、状态查询(status)状态时应该采用的动作。我们可以用service命令手动让脚本切换状态:

$sudo service test start

脚本中相应的动作会被调用。

/etc/init.d/myscript还不能随开机启动。Linux在开机启动时,真正检查的是/etc/rcN.d文件夹,执行其中的脚本。这里的N代表了运行级别。比如说在运行级别2时,Linux会检查/etc/rc2.d文件夹,执行其中的脚本。我们需要把/etc/init.d中的服务复制到或者建立软连接到/etc/rcN.d上,才能让该服务在N运行级别开机时启动。不过,我们可以利用update-rc.d命令更方便地进行,比如在默认的运行级别建立软链接:

    $sudo update-rc.d cron defaults

以及删除默认运行级别下的软链接:

    $sudo update-rc.d cron remove

10.4 避免使用/etc/rc.local

树莓派官网上给出了修改/etc/rc.local的方法,以便在树莓派开机时执行用户自定义的任务。比如在该文件中执行date命令:

    #! /bin/sh -e
    #
    # rc.local
    #
    # This script is executed at the end of each multiuser runlevel.
    # Make sure that the script will "exit 0" on success or any other
    # value on eror.
    #
    # In order to enable or disable this script just change the execution
    # bits.
    #
    # By default this script does nothing.

    # time
    date > /tmp/rc.local.log

    exit 0

但笔者不推荐这种启动方式。/etc/rc.local是在系统初始化的末尾执行的一个脚本。如果把太多的任务加入这个脚本中,不但会拖慢开机速度,还会造成管理上的混乱。因此,/etc/rc.local往往只用于修改一些在启动过程需要设定的参数,而不涉及具体的任务启动。如果想随开机启动某些服务,应该避免使用/etc/rc.local。

10.5 Shell中的定时功能

很多命令自身也带有定时功能,比如关机命令shutdown:

    $sudo shutdown +10

即10分钟后关机。

说明关机的时间:

    $sudo shutdown 22:12

还可以使用sleep命令,让Shell等待一段时间:

    $sleep 10 && echo hello

这里的&&符号连接了两个命令。对于&&符号连接的两个命令,bash会在第一个命令执行成功后才执行第二个。由于第一个命令是让Shell等待10秒,因此输入这行命令后,Shell会在10秒后执行echo hello命令。