bash的并发
默认的情况下,Shell脚本中的命令是串行执行的,必须等到前一条命令执行完后才执行接下来的命令,但是如果我有一大批的的命令需要执行,而且互相又没有影响的情况下(有影响的话就比较复杂了),那么就要使用命令的并发执行了。
未使用并发的脚本
这个脚本是从
etc var dev usr opt home
这几个目录下找文件使用
time sh study.sh
可以看到,执行这个脚本,需要用到0m4.297s
#!/usr/bin/env bash arry=(etc var dev usr opt home) for (( i=0; i<${#arry[@]}; i++ )) do find /${arry[i]}/ -type f >& /dev/null done echo "complate"
简单修改
使用
time sh study.sh
可以看到,执行这个脚本,只需要用到0m0.003s
#!/usr/bin/env bash arry=(etc var dev usr opt home) for (( i=0; i<${#arry[@]}; i++ )) do { find /${arry[i]}/ -type f >& /dev/null } & done echo "complate"
这样的话,循环里面的命令是作为后台进程在执行,主进程就不需要等待前面的命令执行完毕后,才执行后面的命令
如果上下两个命令是有依赖关系的,会得到错误的结果,因为上面的命令还没执行完成,就已经开始执行下面的命令了
使用wait命令
使用
time sh study.sh
可以看到,执行这个脚本,需要用到0m0.492s
#!/usr/bin/env bash arry=(etc var dev usr opt home) for (( i=0; i<${#arry[@]}; i++ )) do { find /${arry[i]}/ -type f >& /dev/null } & done wait echo "complate"
因为使用了wait,要等循环内的命令执行结束了,才会执行后面的命令,因为虚拟机里面文件比较少,所以测试不出并发的差距,甚至因为使用了wait,导致时间比不并发的时间还要多一丢丢
但是如果数据量很大的话,就需要控制并发进程的个数,以免影响到系统的其他进程运行,导致系统宕机
控制并发进程的数量
以下脚本是利用文件描述符号和命名管道实现多进程并发控制,控制并发数量为5
以下脚本会创建254个用户,并且ping这个网段254次
使用
time sh study.sh
可以看到,执行这个脚本,需要用到1m8.744s有兴趣的,可以尝试一下不开启并发,或者不开启并发控制
#!/usr/bin/env bash ip="192.168.100." use="test" # 定义并发数量 thread=5 # 以当前进程 pid 创建 fifo 文件,防止冲突 tmp_fifofile=/tmp/$$.fifo mkfifo ${tmp_fifofile} # 定义当前进程打开管道文件(手动指定打开的文件描述符为8) exec 8<> ${tmp_fifofile} # 删除管道文件(当前进程没有释放文件描述符8,不影响) rm ${tmp_fifofile} # 往管道文件(文件描述符8)里循环写入内容 for i in $(seq ${thread}) do # 给管道文件(文件描述符8)输入${thread}个回车 #(写入任何记录都可以,内容不重要,添加${thread}条记录.重定向,追加都可以,管道文件内容不会被覆盖) echo >&8 done for i in {1..254} do # read -u 读取一次文件描述符的内容 #(read 读取不到内容就停止,管道文件只能读取一次,限制读取${thread}次) read -u 8 { useradd ${use}$i echo "111" | passwd --stdin ${use}$i &>/dev/null if [ $? -eq 0 ]; then echo "$use$i is created" fi ping -c1 -W1 $ip$i &>/dev/null if [ $? -eq 0 ]; then echo "$ip$i is up" else echo "$ip$i is down" fi # 给管道文件(描述符号8)写入一个回车(还会一条记录,内容随意) echo >&8 } & done wait exec 8<&- echo "complate"
文件描述符
- fd 进程打开的文件
查看当前进程打开的文件
ll /proc/$$/fd
自定义当前进程用描述符号操作文件
- 手动定义文件描述符,只要没有被占用可以使用
- 自定义用当前进程打开一个文件,描述符为6
exec 6<> /file
- 自定义用当前进程关闭一个文件,描述符为6
exec 6<&-
管道
- 匿名管道不能跨终端 (| 管道)
- 命名管道
- 创建命名管道文件(不是普通文件,管道内的文件只能读取一次,不会永久保存)
mkfifo /tmp/fifo1
- 查看命名管道文件
cat /tmp/fifo1