在一段时间(或满足其他条件)后终止某个作业,通常涉及两个进程,其中一个进程运行作业而另一个进程监视作业完成条件。通过阅读本文介绍的技巧,您将了解进程如何管理作业实时运行的时间。还将了解在其中一个进程提前结束时如何使用信号和 trap 实用程序终止另一个进程。
定时器进程
shell 脚本中执行定时的基本工具是 sleep 命令,该命令将使运行中的 shell 在指定的一段时间内暂停执行。默认的暂停时间为数秒,但是可以使用 s、m 或 h 命令分别将时间值设置为秒、分钟或小时级别。这将延缓 shell 的执行,因此需要在另一个 shell 中运行实时任务,通过将任务放到使用 & 字符的后台中运行即可达到此目的。
首先,假定您需要使某个命令持续运行 10 分钟。您将编写清单 1 所示的 bash shell 脚本来按照指定的时间段运行 xclock 命令。请在您自己的系统中尝试运行。
清单 1. 首次尝试使用 runclock1.sh
#!/bin/bash
runtime=${1:-10m}
# Run xclock in background
xclock&
#Sleep for the specified time.
sleep $runtime
echo "All done"
您应该能看到类似图 1 所示的时钟。
图 1. 一个简单的 xclock
这种方法惟一的缺陷就是:虽然脚本停止了运行,但是时钟仍然继续运行。
父进程,子进程和孤儿进程(orphan)
清单 2 展示了一个增强的 runclock2.sh 脚本,它在 shell 完成后捕获 shell 和 xclock 进程的进程 id 信息,以及脚本输出和 ps 命令输出,并显示 xclock 的进程状态。
清单 2. 收集诊断信息 runclock2.sh
[ian@attic4 ~]$ cat runclock2.sh
#!/bin/bash
runtime=${1:-10m}
mypid=$$
# Run xclock in background
xclock&
clockpid=$!
echo "My PID=$mypid. Clock’s PID=$clockpid"
ps -f $clockpid
#Sleep for the specified time.
sleep $runtime
echo "All done"
[ian@attic4 ~]$ ./runclock2.sh 10s
My PID=8619. Clock’s PID=8620
UID PID PPID C STIME TTY STAT TIME CMD
ian 8620 8619 0 19:57 pts/1 S+ 0:00 xclock
All done
[ian@attic4 ~]$ ps -f 8620
UID PID PPID C STIME TTY STAT TIME CMD
ian 8620 1 0 19:57 pts/1 S 0:00 xclock
注意:ps 的第一个输出中的父进程 id(PPID)为 8619,这是脚本的进程 id(PID)。当脚本终止后,这个时钟进程将变成孤儿进程并被指定为 init 进程(进程 1)的子进程。当父进程终止时,子进程不会立即终止,但是它将在用户注销系统后终止。
终止子进程
终止子进程的解决方法就是使用 kill 命令进行显式终止。它将向进程发送一个信号,这通常都会使进程终止。稍后您将看到进程如何 trap 信号而使终止失败,但是这里我们将使用终止信号(SIGINT)来终止时钟。
要查看系统可用的信号列表,请使用 kill 命令和 -l 选项,如清单 3 所示。注意:有些信号可在所有 Linux 系统中通用,而有些信号只能用于特定的机器架构。其中一些信号是由系统生成,例如 floating point exceptions (SIGFPE) 或 segment violation (SIGSEGV),而另一些信号则由应用程序发送,例如 interrupt (SIGINT)、user signals (SIGUSR1 或 SIGUSR2) 或 unconditional terminate (SIGKILL)。
清单 3. Fedora Core 5 系统中的信号
[ian@attic4 ~]$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
在清单 2 中,我们使用 $! shell 变量捕获到 xclock 进程的 PID。清单 4 展示了如何使用这些信息向 xclock 进程发送终止信号情报(SIGTERM),从而终止该进程。
清单 4. 使用 runclock3.sh 终止子进程 runclock3.sh
[ian@attic4 ~]$ cat ./runclock3.sh
#!/bin/bash
runtime=${1:-10m}
mypid=$$
# Run xclock in background
xclock&
clockpid=$!
echo "My PID=$mypid. Clock’s PID=$clockpid"
ps -f $clockpid
#Sleep for the specified time.
sleep $runtime
kill -s SIGTERM $clockpid
echo "All done"
清单 5 展示了在执行 runclock3.sh 时发生的操作。最后的 kill 命令确定 xclock 进程(PID 9285)确实已终止。
清单 5. 验证子进程是否终止
[ian@attic4 ~]$ ./runclock3.sh 5s
My PID=9284. Clock’s PID=9285
UID PID PPID C STIME TTY STAT TIME CMD
ian 9285 9284 0 22:14 pts/1 S+ 0:00 xclock
All done
[ian@attic4 ~]$ kill -0 9285
bash: kill: (9285) - No such process
如果没有查看信号规范,那么这里稍作说明:SIGTERM 是默认信号。信号名称中的 SIG 部分是可选的。无需使用 -s 和信号名,只需在信号数之前加上 - 前缀,因此清单 6 中显示的四种方法都能够终止进程 9285。注意特殊值 -0,正如清单 4 中的用法一样,它将测试是否可以将某个信号发送到进程。
清单 6. 使用 kill 命令指定信号的方法
kill -s SIGTERM 9285
kill -s TERM 9285
kill -15 9285
kill 9285
其他终止条件
现在您已经具备了基本的工具来控制进程运行的时间。在继续深入了解信号处理之前,让我们首先查看一下如何处理其他终止需求,例如在有限的时间内不断捕获信息,直到文件大小达到某个值时终止作业,或者当文件包含特定字符串时终止作业。循环可以很好地执行这类任务,例如 for、while 或 until,同时使用 sleep 命令提供的内置延迟反复执行循环。如果您需要使用低于秒的时间粒度,还可以使用 usleep 命令。
您还可以向时钟添加秒针,并自定义颜色。使用 showrgb 命令可以查看可用的颜色名称。假设使用 xclock -bg Thistle -update 1& 命令启动背景为蓟色的具有秒针的时钟。
现在您可以使用循环结合已学到的知识捕获每秒的时钟外观图像,然后组合这些图像来制作动画 GIF 图像。清单 7 展示了如何使用 xwininfo 命令查找 xclock 命令的窗口 id。然后使用 ImageMagick 命令行工具捕获 60 个时钟外观图像(时间间隔为 1 秒)(参阅 参考资料 中有关 ImageMagick 的详细内容)。最后,将这些图像组合成无限循环的 GIF 文件,其大小是原始时钟的一半。
清单 7. 每秒捕获一次图像
[ian@attic4 ~]$ cat getclock.sh
#!/bin/bash
windowid=$(xwininfo -name "xclock"| grep ’"xclock"’ | awk ’{ print $4 }’)
sleep 5
for n in `seq 10 69`; do
import -frame -window $windowid clock$n.gif&
sleep 1s
# usleep 998000
done
convert -resize 50% -loop 0 -delay 100 clock?[0-9].gif clocktick.gif
[ian@attic4 ~]$ ./getclock.sh
[ian@attic4 ~]$ file clocktick.gif
clocktick.gif: GIF image data, version 89a, 87 x 96
这种类型的计时总是会发生变化,因此将获取时钟图像的 import 命令放在后台中运行,从而避免使用主要的 shell 脚本跟踪时间。但是,这个过程会发生一些时间偏差,因为需要花费一些时间启动每个子 shell 脚本来进行后台处理。本例中存在一个 5 秒的延迟,即从开始启动 shell 脚本,然后单击时钟使其显示在桌面上,这一过程需要花费 5 秒钟的时间。即使注意了这些事项,时钟运行时仍然丢失了一个滴答,并额外复制了一个起始的滴答,因为脚本的运行略微超过了 60 秒。解决这种问题的一种方法是使用时间粒度为微秒级的 usleep 命令,这种时间粒度远远小于 1 秒,从而解决延迟问题。如脚本中的注释行所示。如果一切如预期运行,那么输出图像应该类似于图 2 所示。
图 2. 滴答中的 xclock
这个例子展示了如何以固定时间间隔获取一定数量的系统条件快照。使用本文所述的技巧,您可以获取其他系统条件的快照。您可能希望检查输出文件的大小以确保没有超出限制,或者检查某个文件是否包含了特定的消息,或者使用 vmstat 这样的命令检查系统状态。您可以尽情发挥自己的想象力。
.
分页: [1] [2]
TAG: Linux作业