SHELL脚本“多线程”

最近完善优化inotify脚本,由于队列长时会有很长等待,于是考虑多线程实现。

由于所要执行的程序本身就是在循环里面的,所以以下面脚本示例:

[root@Test ~]# vim t.sh
#!/bin/bash
get_time () {
        date +%H:%M:%S | awk -F : '{print $1*3600 + $2*60 + $3}'
}
pre_time=`get_time`
for i in {1..10}
do
        last_time=`get_time`
        sleep 3
        now_time=`get_time`
        dint=$((${now_time} - ${last_time}))
        echo cycle $i use time $dint total use time $((${now_time} - ${pre_time}))
done
[root@Test ~]# chmod +x t.sh 
[root@Test ~]# ./t.sh 
cycle 1 use time 3 total use time 3
cycle 2 use time 3 total use time 6
cycle 3 use time 3 total use time 9
cycle 4 use time 3 total use time 12
cycle 5 use time 3 total use time 15
cycle 6 use time 3 total use time 18
cycle 7 use time 3 total use time 21
cycle 8 use time 3 total use time 24
cycle 9 use time 3 total use time 27
cycle 10 use time 3 total use time 30

可以看到循环10次用了30秒,shell脚本中实现多线程的方法其实是将需要多线程执行的任务放入后台执行:

[root@Test ~]# vim t.sh
#!/bin/bash
get_time () {
        date +%H:%M:%S | awk -F : '{print $1*3600 + $2*60 + $3}'
}
pre_time=`get_time`
for i in {1..10}
do
        {
        last_time=`get_time`
        sleep 3
        now_time=`get_time`
        dint=$((${now_time} - ${last_time}))
        echo cycle $i use time $dint total use time $((${now_time} - ${pre_time}))
        } &
done
[root@Test ~]# ./t.sh ; ps
 PID TTY TIME CMD
 5005 pts/0 00:00:00 bash
 5736 pts/0 00:00:00 t.sh
 5738 pts/0 00:00:00 sleep
 5741 pts/0 00:00:00 t.sh
 5743 pts/0 00:00:00 sleep
 5746 pts/0 00:00:00 t.sh
 5748 pts/0 00:00:00 sleep
 5751 pts/0 00:00:00 t.sh
 5753 pts/0 00:00:00 sleep
 5756 pts/0 00:00:00 t.sh
 5758 pts/0 00:00:00 sleep
 5761 pts/0 00:00:00 t.sh
 5763 pts/0 00:00:00 sleep
 5766 pts/0 00:00:00 t.sh
 5768 pts/0 00:00:00 sleep
 5771 pts/0 00:00:00 t.sh
 5773 pts/0 00:00:00 sleep
 5776 pts/0 00:00:00 t.sh
 5778 pts/0 00:00:00 sleep
 5781 pts/0 00:00:00 t.sh
 5782 pts/0 00:00:00 sleep
 5783 pts/0 00:00:00 ps
cycle 1 use time 3 total use time 3
cycle 2 use time 3 total use time 3
cycle 3 use time 3 total use time 3
cycle 4 use time 3 total use time 3
cycle 5 use time 3 total use time 3
cycle 6 use time 3 total use time 3
cycle 7 use time 3 total use time 3
cycle 8 use time 3 total use time 3
cycle 9 use time 3 total use time 3
cycle 10 use time 3 total use time 3

可以看到,10次循环全部一起执行了。

如需有效控制后台进程数量,需要使用mkfifo、exec这两个命令。

  • mkfifo 使用指定的文件名创建FIFO(也称为”命名管道”).

“FIFO”是一种特殊的文件类型,它允许独立的进程通讯. 一个进程打开FIFO文件进行写操作,而另一个进程对之进行读操作, 然后数据便可以如同在shell或者其它地方常见的的匿名管道一样流线执行.

如,创建一个fifo文件,将screen log“记录”到里面,在另一个终端上查看:

[root@Test ~]# mkfifo sc.log
[root@Test ~]# script -f sc.log

之后屏幕是处于等待状态,要有另一个进程在读方可正常,打开另一个终端进程查看:

[root@Test ~]# cat sc.log 
Script started on 2016年07月18日 星期一 13时39分43秒

这时便已开始记录。

fifo

这进第二个终端是处于查看状态,是不接受命令的,在第一个终端使用CRTL+D可进行退出:

fifo2

  • exec介绍:http://blog.csdn.net/cyberrusher/article/details/7253385

exec和source都属于bash内部命令(builtins commands),source命令是在当前进程中执行参数文件中的各个命令,而不是另起子进程(或sub-shell)。exec命令在执行时会把当前的shell process关闭,然后换到后面的命令继续执行。

举个栗子:

exec

可以看到,执行完exec ls之后自动退出了。

[root@Test ~]# ls
1.sh  anaconda-ks.cfg  apache-tomcat-8.0.28.tar.gz  inotify.sh  inotify-tools-3.14.tar.gz  install.log  install.log.syslog  jdk-8u91-linux-x64.tar.gz  t.sh
[root@Test ~]# exec > test
[root@Test ~]# ls
[root@Test ~]# pwd
[root@Test ~]# echo "hello world"
[root@Test ~]# exec > /dev/tty
[root@Test ~]# ls
1.sh  anaconda-ks.cfg  apache-tomcat-8.0.28.tar.gz  inotify.sh  inotify-tools-3.14.tar.gz  install.log  install.log.syslog  jdk-8u91-linux-x64.tar.gz  test  t.sh
[root@Test ~]# cat test 
1.sh
anaconda-ks.cfg
apache-tomcat-8.0.28.tar.gz
inotify.sh
inotify-tools-3.14.tar.gz
install.log
install.log.syslog
jdk-8u91-linux-x64.tar.gz
test
t.sh
/root
hello world
[root@Test ~]#

exec >text 是将当前shell的标准输出都打开到text文件中。

exec I/O重定向, I/O重定向通常与 FD文件描述符有关,shell的FD通常为10个,即 0~9;在/dev/fd/目录下可以看到系统预定义的FD。

[root@Test ~]# ll /dev/fd/
总用量 0
lrwx------. 1 root root 64 7月 18 11:12 0 -> /dev/pts/0
lrwx------. 1 root root 64 7月 18 11:12 1 -> /dev/pts/0
lrwx------. 1 root root 64 7月 18 11:12 2 -> /dev/pts/0
lr-x------. 1 root root 64 7月 18 11:12 3 -> /proc/8511/fd
[root@Test ~]#

0为标准输入,通常为键盘。

1为标准输出

2为标准错误输出

3为当前进程

创建一个新的描述附FD,可以使用exec 4>point命令,这样就相当于创建了一个fd4的管道,如:

[root@Test ~]# exec 4>/root/point
[root@Test ~]# ls
1.sh             apache-tomcat-8.0.28.tar.gz  inotify-tools-3.14.tar.gz  install.log.syslog         point
anaconda-ks.cfg  inotify.sh                   install.log                jdk-8u91-linux-x64.tar.gz  t.sh
[root@Test ~]# ll /dev/fd/
总用量 0
lrwx------. 1 root root 64 7月  18 11:37 0 -> /dev/pts/0
lrwx------. 1 root root 64 7月  18 11:37 1 -> /dev/pts/0
lrwx------. 1 root root 64 7月  18 11:37 2 -> /dev/pts/0
lr-x------. 1 root root 64 7月  18 11:37 3 -> /proc/8572/fd
l-wx------. 1 root root 64 7月  18 11:37 4 -> /root/point
[root@Test ~]#

这时向管道4里丢入数据,都会重定向到point文件中,关闭这个FD使用exec 4>&-命令:

[root@Test ~]# w >&4
[root@Test ~]# ls >&4
[root@Test ~]# cat point 
 11:43:47 up 36 days, 13 min,  3 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
root     tty1     -                12Jun16 35days  0.26s  0.26s -bash
root     pts/0    172.16.*.182   11:34    0.00s  0.08s  0.04s w
root     pts/1    172.16.*.182   11:22   20:56   0.02s  0.02s -bash
1.sh
anaconda-ks.cfg
apache-tomcat-8.0.28.tar.gz
inotify.sh
inotify-tools-3.14.tar.gz
install.log
install.log.syslog
jdk-8u91-linux-x64.tar.gz
point
t.sh
[root@Test ~]# exec 4>&-
[root@Test ~]# ll /dev/fd/
总用量 0
lrwx------. 1 root root 64 7月  18 11:45 0 -> /dev/pts/0
lrwx------. 1 root root 64 7月  18 11:45 1 -> /dev/pts/0
lrwx------. 1 root root 64 7月  18 11:45 2 -> /dev/pts/0
lr-x------. 1 root root 64 7月  18 11:45 3 -> /proc/8579/fd
[root@Test ~]#

通道4已关闭。

上面是小插曲,言归正转,控制后台进程数量:

[root@Test ~]# vim t.sh
#!/bin/bash
thread=2
get_time () {
        date +%H:%M:%S | awk -F : '{print $1*3600 + $2*60 + $3}'
}
# 创建FIFO文件
fifofile="/tmp/$.fifo"
mkfifo ${fifofile}
# 定义FD4并指向此文件
exec 4<>${fifofile}
rm -rf ${fifofile}
# 向FD4中放置了${thread}个空格
for ((p=0;p<${thread};p++))
do
        echo
done>&4
pre_time=`get_time`
for i in {1..10}
do
        {
        # 从FD4中读取(占用)一个空行,FD4中没有空行时就会停在此处,从而控制线程数量
        read -u 4
        last_time=`get_time`
        sleep 3
        now_time=`get_time`
        dint=$((${now_time} - ${last_time}))
        echo cycle $i use time $dint total use time $((${now_time} - ${pre_time}))
        # 补充回一个空行
        echo >&4
        } &
done
exec 4>&-

为了方便理解,我将上面的几条单独拿出来执行,即可看到现象:

mkfifo_exec

执行我们的脚本:

[root@Test ~]# ./t.sh 
cycle 1 use time 3 total use time 3
cycle 2 use time 3 total use time 3
cycle 10 use time 3 total use time 6
cycle 3 use time 3 total use time 6
cycle 7 use time 3 total use time 9
cycle 6 use time 3 total use time 9
cycle 4 use time 3 total use time 12
cycle 9 use time 3 total use time 12
cycle 8 use time 3 total use time 15
cycle 5 use time 3 total use time 15

[root@Test ~]#

已达预期效果,后台线程每两个两个进行执行。

发表评论

error: Content is protected !!