分享

Shell脚本中的多任务并发执行

本帖最后由 hyj 于 2021-1-19 20:00 编辑


导读:
面试中,有些成员被问到了,shell并发执行,那么shell是串行执行的,那么该如何并发执行,下面大家可以找到相关问题的答案。

——————————————

Shell脚本中的多任务并发执行
需求
实现一个脚本能够使用ping检测网络的连通性
可以同时检测多个IP地址, 并且将检测结果输出

分析
正常情况下,Shell脚本中的命令是串行执行的,当一条命令执行完才会执行接下来的命令。例如:

  1. #!/bin/bash
  2. for i in {1..10};do
  3.         echo $i
  4. done
  5. echo "END"
复制代码
结果如下:
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. END
复制代码
可见,循环体中的“echo $i”命令是串行执行的。但是如果所执行的命令耗时比较长,这就会导致整个程序的执行时间非常长,甚至可能导致程序执行时卡住而失去响应。

这里,我们的需求是:编写一个脚本,扫描192.168.0.0/24网络里某个区间网段有哪些主机在线,能ping通就认为在线。下面是写好的代码:


  1. #!/bin/bash
  2. for i in {65,66,67,72,73,68};do
  3.         ip="192.168.0.$i"
  4.         ping -c 2 $ip &> /dev/null && echo $ip is up
  5. done
复制代码
这里对脚本中使用的ping命令稍作说明:Linux中的ping命令在执行后会连续不断地发包,而且使用了“-c”选项,指定只发2次包,如果能收到响应,就认为目标主机在线。

从逻辑上看,这个脚本没有问题,但是由于要对网络中的多个IP地址轮流执行ping命令,在执行后耗时将非常长,而且此时的脚本无法使用Ctrl+C强制终止,只能使用Ctrl+Z转入后台,然后再用kill命令强制结束进程。

  1. [root@localhost ~]# bash pinghost.sh
  2. 192.168.0.65 is up
  3. 192.168.0.66 is up
  4. ^C
  5. ^Z
  6. [1]+  已停止               bash pinghost.sh
  7. [root@localhost ~]# jobs -l                             #查看后台工作任务
  8. [1]+ 12496 停止                  bash pinghost.sh
  9. [root@localhost ~]# kill -9 12496                      #强制结束进程
  10. [root@localhost ~]#
  11. [1]+  已杀死               bash pinghost.sh
复制代码
实际上,在这个脚本中所循环执行的ping命令之间并没有依赖关系,也就是说不必非要等到“ping 192.168.0.65”结束之后才能接着执行“ping 192.168.0.66”,所有的这些ping命令完全可以并发执行。

如果是使用Python,则可以借助于多线程技术来实现命令的并发执行,而Shell不支持多线程,因此只能采用多进程的方式。具体的实现方法就是在要并发执行的命令后面加上“&”,将其转入后台执行,这样就可以在执行完一条命令之后,不必等待其执行结束,就立即转去执行下一条命令。修改后的代码如下:

  1. #!/bin/bash
  2. for i in {1..10};do
  3.         echo $i &
  4. done
  5. echo "END"
复制代码
执行结果如下:
  1. 1
  2. 2
  3. 3
  4. 4
  5. 8
  6. END
  7. 5
  8. 10
  9. 6
  10. 9
复制代码
可见,在并发执行时不能保证命令的执行顺序,而且本应在整个循环执行结束之后再执行的echo "END"命令,却在程序一开始就被执行了。所以在并发执行时,我们通常都需要保证在循环体中的所有命令都执行完后再向后执行接下来的命令,这时就可以使用 wait命令来实现。
在Shell中使用wait命令,相当于其它高级语言里的多线程同步。修改代码如下:



  1. #!/bin/bash
  2. for i in {1..10};do
  3.         echo $i &
  4. done
  5. wait
  6. echo "END"
复制代码
此时的执行结果如下:
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 8
  7. 6
  8. 9
  9. 7
  10. 10
  11. END
复制代码
这样执行结果就正常了。

代码
回到我们的问题上来,如果要检测多个IP的网络连通性,也可以使用上述过程的思路,代码如下:


  1. #!/bin/bash
  2. for i in {65,66,67,72,73,68};do
  3.         ip="192.168.0.$i"
  4.         ping -c 2 $ip &> /dev/null && echo $ip is up &
  5. done
  6. wait
复制代码
网络的检测结果如下:
  1. [root@localhost ~]# bash pinghost.sh
  2. 192.168.0.65 is up
  3. 192.168.0.66 is up
  4. 192.168.0.72 is up
  5. 192.168.0.73 is up
  6. 192.168.0.67 is up
  7. 192.168.0.68 is up
复制代码



因此,当在循环执行的命令之间没有依赖关系时,完全可以采用并发执行的方式,这样可以大幅提高代码执行效率。

当然,并发执行也有缺陷,就是当需要并行执行的命令数量特别多,特别是所执行的命令占用的系统资源非常多时,可能会将整个系统的资源全部耗尽,影响其它程序的运行,因此还可以借助其它技术来限制并发执行的进程数量。




原文链接:
https://blog.csdn.net/arpospf/article/details/105290642

已有(1)人评论

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条