在Shell中是一条命令执行完成才会执行下一条命令。
举例:这里拿测试同网段内所有IP是否连通为例

正常情况下:

[root@localhost Code]# vim ping.sh
#!/bin/bash

for i in {1..254}
do
        IP=192.168.1.$i
        ping -c1 -W1 $IP &> /dev/null
        if [ $? -eq 0 ];        then
                echo "$IP is up"
        else
                echo "$IP is down"
        fi
done
echo "all finish..."

脚本执行结果:

# 现在看到是顺序执行的
[root@localhost Code]# chmod a+x ping.sh
[root@localhost Code]# time ./ping.sh
192.168.1.1 is down
192.168.1.2 is up
192.168.1.3 is down
192.168.1.4 is down
192.168.1.5 is down
192.168.1.6 is down
192.168.1.7 is down
192.168.1.8 is down
192.168.1.9 is down
192.168.1.10 is down
192.168.1.11 is up
192.168.1.12 is down
...
all finish...

real    4m10.934s
user    0m0.246s
sys 0m0.559s

可以看出在正常情况下脚本的运行是很慢的(最后用了4m10.934s才完成)
这个时候我们就可以用多进程的方式来减少脚本的运行时间

多进程

#!/bin/bash

for i in {1..254}
do
        {
        IP=192.168.1.$i
        ping -c1 -W1 $IP &> /dev/null
        if [ $? -eq 0 ];        then
                echo "$IP is up"
        else
                echo "$IP is down"
        fi
        }&
done
wait        # 等待wait上面的后台进程完成再执行后面的操作
echo "all finish..."

wait命令:是用来等待指令的指令,直到其执行完毕后返回终端。该指令常用于shell脚本编程中,待指定的指令执行完成后,才会继续执行后面的任务。该指令等待作业时,在作业标识号前必须添加备份号”%”。

脚本执行结果:

# 加入多进程以后,就会发现已经并发执行了(不按顺序)
[root@localhost Code]# time ./ping.sh
192.168.1.11 is up
192.168.1.2 is up
192.168.1.22 is up
192.168.1.33 is up
192.168.1.28 is down
192.168.1.30 is down
192.168.1.13 is down
192.168.1.15 is down
192.168.1.16 is down
192.168.1.18 is down
192.168.1.21 is down
...
all finish...

real    0m2.105s
user    0m0.147s
sys 0m1.031s

这次就明显看出多进程情况下,脚本的执行时间缩短了很多(最后仅用了2.105s就完成了所有操作)

虽然在进行批量处理时,这种多进程的可以很快的处理完成,但是在并发特别多的时候,就会导致资源占用超标,从而影响其他程序的运行

举例:比如说想要创建1000个用户

[root@localhost Code]# vim useradd.sh
#!/bin/bash
for i in {1..1000}
do
        {
        user=test$i
        useradd $user
        echo "111" | passwd --stdin $user &> /dev//null
        if [ $? -eq 0 ];        then
                echo "$user is created..."
        fi
        }&
done
wait
echo "finish..."

脚本执行结果

# 当在服务器配置不够高的时候,就有可能会出现下面这种情况
[root@localhost Code]# chmod a+x useradd.sh
[root@localhost Code]# ./useradd.sh
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。
useradd:无法锁定 /etc/passwd,请稍后再试。

所以就需要控制并发,来解决这种情况的发生

并发控制

文件描述符

File Descriptors(FD,文件描述符,文件句柄)

查看文件描述符

$$:当前进程

[root@localhost ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 4月  21 14:41 0 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 1 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 2 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 255 -> /dev/pts/2

创建文件描述符

6:文件描述符

# 创建FD
[root@localhost ~]# exec 6<> /file1
# 查看FD
[root@localhost ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 4月  21 14:41 0 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 1 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 2 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 255 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 16:36 6 -> /file1

修改文件描述符内容

[root@localhost ~]# echo "shuaiguoer.com" >> /file1
[root@localhost ~]# cat /file1
shuaiguoer.com

删除文件(文件描述符还在)

[root@localhost ~]# rm -rf /file1
[root@localhost ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 4月  21 14:41 0 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 1 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 2 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 255 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 16:36 6 -> /file1 (deleted)

通过文件描述符恢复被删除的文件

[root@localhost ~]# cp /proc/$$/fd/6 /file1
[root@localhost ~]# cat /file1
shuaiguoer.com

关闭文件描述符

[root@localhost ~]# exec 6<&-

[root@localhost ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 4月  21 14:41 0 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 1 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 2 -> /dev/pts/2
lrwx------. 1 root root 64 4月  21 14:41 255 -> /dev/pts/2

管道

管道特征:一个命令的输出,作为下一个命令的输入

管道文件和普通文件的区别

管道文件:不是永久存在的,读取完以后就没了(内存中)
普通文件:是永久存在的(硬盘中)

匿名管道

只能在同一个终端使用

[root@localhost ~]# rpm -qa | grep bash

命名管道

#可以跨终端使用
#####创建命名管道
[root@localhost ~]# mkfifo /tmp/fifo1
# 查看文件类型
[root@localhost ~]# file /tmp/fifo1
/tmp/fifo1: fifo (named pipe)
# tty1
# 现在是阻塞的...
[root@localhost ~]# grep "sda" /tmp/fifo1
# tty2
#输入内容到命令管道
[root@localhost ~]# ll /dev/ > /tmp/fifo1
# 再次回到tty1
# 就会发现完成了
[root@localhost ~]# grep "sda" /tmp/fifo1 
brw-rw----. 1 root disk      8,   0 4月  21 11:23 sda
brw-rw----. 1 root disk      8,   1 4月  21 11:23 sda1
brw-rw----. 1 root disk      8,   2 4月  21 11:23 sda2

# 再次使用管道后,又会阻塞终端,除非再次往管道里面输入内容
[root@localhost ~]# grep "sda" /tmp/fifo1

并发控制实例

通过使用 管道文件 和 令牌原理实现的并发控制。

#!/bin/bash

process=10      # 最大进程并发数
fifo_file=/tmp/$$.fifo      # 命名管道文件名称

mkfifo $fifo_file       # 创建命令管道
exec 8<> $fifo_file     # 创建文件描述符,以可读(<)可写(>)的方式关联管道文件
rm $fifo_file       # 删除管道文件(需要用到的文件描述符是还在的)

# 创建令牌
for i in `seq $process`
do
        echo >&8        # 每次输入一个换行符,也就是一个令牌
done

for i in {1..1000}
do
        read -u 8        #read -u8命令执行一次,相当于尝试从fd8中获取一行,如果获取不到,则阻塞
        #获取到了一行后,fd8就少了一行了,开始处理子进程,子进程放在后台执行
        {
        user=user$i
        useradd $user
        echo "123.com" | passwd --stdin $user &> /dev//null
        if [ $? -eq 0 ];        then
                echo "$user is created..."
        fi
        #完成后再补充一个回车到fd6中,释放一个锁
        echo >&8        # 当进程结束以后,再向fd6中加上一个回车符,即补上了read -u8减去的那个
        }&
done
wait
exec 8>&-       # 关闭文件描述符的写
echo "all finish..."

脚本执行结果

# 这样每次就只会同时创建10个用户
[root@localhost Code]# ./useradd_multi_thread01.sh
user3 is created
user7 is created
user8 is created
user4 is created
user9 is created
user6 is created
user1 is created
user5 is created
user10 is created
user2 is created
user11 is created
user12 is created
user13 is created
user14 is created
user15 is created
user16 is created
user17 is created
...
all finish...
Last modification:July 23rd, 2020 at 04:24 pm
如果觉得我的文章对你有用,请随意赞赏