【shell脚本】实现并发编程


参考:
https://blog.csdn.net/u011436427/article/details/104829385
https://blog.csdn.net/Simpletwt/article/details/87688239
https://blog.csdn.net/weixin_34242819/article/details/89860708

1 前置知识

实际上我们可以通过命名管道实现shell脚本可控的并发编程。

1.0 文件描述符

File Descriptors(FD): 文件描述符或者文件句柄
进程使用文件描述符来管理打开的文件

# 查看本进程的fd
ls /proc/$$/fd

1.1 命名管道

# mkfifo 创建命名管道的命令
mkfifo /tmp/fifo1
# 
exec 8<> /tmp/fifo1
#创建文件描述符8关联管道文件,这时候8这个文件描述符就拥有了管道的所有特性,还具有一个管道不具有的特性:无限存不阻塞,无限取不阻塞,而不用关心管道内是否为空,也不用关心是否有内容写入引用文件描述符

# 通过fd:8向命名管道中写入数据
echo "1" >&8  
echo "2" >&8
echo "3" >&8

1.2 读取数据

编写下面的脚本即可从管道中读取相关数据。read -u8 i 的意思是从 8 号 fd (file descriptor,文件描述符) 中读一行数据到 i 变量中

# test-read.sh
(read -u8 i
echo "$?"
echo "读取的数据:$i")&

2 并发脚本

下面我们以测试主机是否能连通为例,编写对应的shell脚本。
我们可以发现下面3个脚本的区别,以速度来说,第2种方法最快,但是会在一瞬间占用大量的cpu资源,第3种方法最可控,但是执行速度比纯粹串行的快,而比第2种方法慢。

2.1 无并发的脚本

#test-ping-1.sh
#!/bin/bash
start=`date +%s`

for ip in 192.168.0.{1..255}
do
  ping $ip -c 2 &> /dev/null
  if [ $? -eq 0 ]
  then
    echo "$ip is alive"
  else
    echo "$ip is unreachable"
  fi
done

end=`date +%s`
cost=$[ $end - $start ]
echo "cost :$cost"

2.2 不受控的并发脚本

#test-ping-2.sh
#!/bin/bash
start=`date +%s`

for ip in 192.168.0.{1..255}
do
  {
   ping $ip -c 2 &> /dev/null
  if [ $? -eq 0 ]
  then
    echo "$ip is alive"
  else
    echo "$ip is unreachable"
  fi
  }&
done
# wait等待所有的后台进程执行结束,如果不加wait,则父进程会不等其他子进程全部执行完毕,就会退出
# 因此为了等待所有子进程全部执行完毕,需要加wait
wait
end=`date +%s`
cost=$[ $end - $start ]
echo "cost :$cost"

2.3 受控的并发脚本

通过结合命名管道,可以实现受控的并发脚本。

#test-ping-3.sh
#!/bin/bash
start=`date +%s`

thread_num=5
tmp_fifofile=/tmp/$$.fifo

mkfifo $tmp_fifofile
#创建文件描述符,以可读(<)可写(>)的方式关联管道文件,这时候文件描述符8就有了有名管道文件的所有特性
exec 8<> $tmp_fifofile
#关联后的文件描述符拥有管道文件的所有特性,所以这时候管道文件可以删除
rm -f $tmp_fifofile

# 往命名管道中写5个空行
for i in `seq $thread_num`
do
  echo >&8
done

# ping主机
for ip in 192.168.0.{1..255}
do
  {
   # 从fd:8中读取一个空行
   read -u 8
   ping $ip -c 2 &> /dev/null
   if [ $? -eq 0 ]
   then
    echo "$ip is alive"
   else
    echo "$ip is unreachable"
   fi
  # 进程执行完毕,再向命名管道中写入一个空行
    echo >&8
  }&
done
# wait会等待所有的后台进程执行结束,如果不加wait,则父进程会不等其他子进程全部执行完毕,就会退出
# 因此为了等待所有子进程全部执行完毕,需要加wait
wait
#关闭文件描述符的写
exec 8>&-
#关闭文件描述符的读
exec 8<&-
end=`date +%s`
cost=$[ $end - $start ]
echo "cost :$cost"

相较于第2种方法,该方法比较慢,由于设置进程数目为5,每次都会生成5个子进程同时进行操作,每次在stdout都是5个结果一起输出。也正是因为如此,所以不会导致cpu资源不会被该脚本大量占用。
此时我们可以另外打开一个终端,输入命令如下命令进行监控。可以发现一共6个进程,除了第一个进程,其他5个进程的父进程都是第一个进程的pid。

watch "ps -ef | grep test-ping-3.sh"
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值