(4.14)Fd和命名管道实现shell并发控制

1.shell并发控制之描述符

  • File Descriptors(FD,文件描述符)或者文件句柄
    进程使用文件描述符来管理打开的文件
ls /proc/$$/fd
0 1 2 3 4
0:标准输入
1:标准输出
2:标准错误输出

在这里插入图片描述

在这里插入图片描述

  • exec打开一个文件
touch /file1
exec 6 <> /file1##手动打开了一个文件描述符,给他一个6描述符

在这里插入图片描述

  • 因为文件句柄没有释放,就算删除/file1文件,但是文件描述符还在 ,所以可以copy出来,但是前后两个file1文件的inode编号不一样了!
    当一个文件FD未释放,删除源文件也不会影响FD
    在这里插入图片描述
    虽然cp复原了,但是文件/file1的inode发生了改变,还是会显示deleted状态
    在这里插入图片描述
  • exec关闭一个文件(释放一个文件句柄
exec 6<&-##关闭一个文件描述符,释放掉当前进程的文件句柄6

在这里插入图片描述

2.shell并发控制之管道

  • 管道是一个文件
  • 匿名管道:你放到里面的内容,被别人拿走就没了;在一个终端(进程)玩
rpm -qa|grep bash
  • 命名管道:先进先出,内容读完就没了;两个终端(进程)去玩
    命名管道,就是有名字的管道,eg: /tmp/fifo1
mkfifo /tmp/fifo1
file /tmp/fifo1
ll /dev/ > /tmp/fifo1

接着查看管道中的内容
grep 'sda' /tmp/fifo1

3.并发控制样例

  • eg1:
#ping01
#!/usr/bin/bash
thread=5#进程数量
tmp_fifofile=/tmp/$$.fifo

mkfifo $tmp_fifofile
exec 8<> $tmp_fifofile
rm $tmp_fifofile		##描述符8还存在,是否删除tmp_fifofile对fd=8没有影响

for i in `seq $thread`
do
	##这里用>>追加是没有意义的,因为这是管道,不是常规文件,所以这里不可能会覆盖前面的内容的,后面
	##增加的内容对前面没有影响
	echo >&8 ##&8指的就是文件描述符8,单个echo就是回车。echo "111" > &8
	##该文件tmp_fifofile里面有5个回车
done


for i in {2..254}
do
	##read --help,-u跟文件描述符
	##读一个回车,就循环,循环到第6次的时候,就跳出read了,因为没得读了
	##read会将管道中的5个空格都读完,读完才跳出read,但是每次读完一个空格又给管道塞一个空格
	read -u 8
	{
		ip=192.168.122.$i
		ping -c1 -W1 $ip &>/dev/null  ## -W1表示1秒超时
		if [ $? -eq 0 ];then
			echo "$ip is up"
		else
			echo "$ip is down"
		fi
		echo >&8##上面的事做完了,就还回去。不要都借完了,再还回去,保证管道的输入有5个数据输入
	}&
done
wait ##等待所有的后台进程执行结束,如果不加这里,有些ping执行不成功,就会执行echo "finish"
exec 8>&- 
echo "finish"

左图中的横杠表示5个回车,read 从fd=8中去读,读完为止
在这里插入图片描述

  • 测试:执行的话,5个,5个一起出结果
    在这里插入图片描述

4.别人的并发

  • 串行执行:
#!/bin/bash
start=`date +%s`
 
for i in $(seq 1 5); do
  echo test
  sleep 2
done
 
end=`date +%s`
 
time=$(($end - $start))
echo "time: $time" 
  • 测试:
# sh test1.sh
test
test
test
test
test
time: 10
  • shell中实现多进程实际上就是将多个任务放到后台中执行而已
    (1)如果可以一次执行多个任务的,把它放到后台即可:
    (2)说下改动,首先我把for循环中的代码用{}包为一个块,然后增加&符号使其后台运行,之后增加wait指令,该指令会等待所有后台进程结束,如果不加wait,程序直接往下走,最终打出的time将会是0

#!/bin/bash
start=`date +%s`
 
for i in $(seq 1 5); do
  {
    echo test
    sleep 2
  }&
done
 
wait
 
end=`date +%s`
 
time=$(($end - $start))
  • 测试:
# sh test2.sh
test
test
test
test
test
time: 2
  • 管道实现并发
#!/bin/bash
 
 ##首先创建一个管道文件tm1,然后使用exec命令将该文件的输入输出绑定到5号文件描述符,而后删除该管道文件,
 这里删除的只是该文件的Inode,实际文件已由内核open函数打开,这里删除并不会影响程序执行,
 当程序执行完后,文件描述符会被系统自动回收。
mkfifo tm1
exec 5<>tm1
rm -f tm1
 
 ##通过一个for循环向该文件描述符写入10个空行,这个10其实就是我们定义的后台进程数量,
 这里需要注意的是,管道文件的读取是以行为单位的。
for ((i=1;i<=10;i++)); do
  echo >&5
done
 
这里假定我后台有100个任务,而我们在后台保证只有10个进程在同时运行
read -u5 的作用是,读取5号文件描述符中的一行,就是读取一个空行
在减少文件描述符的一个空行之后,在后台执行一次任务,而任务在执行完成以后,会向文件描述符中再写入一个空行,
这是为什么呢,因为如果我们写入空行的话,当后台放入了10个任务之后,由于没有可读取的空行,read -u5就会被阻塞住!
for ((j=1;j<=100;j++)); do
  read -u5
  {
    echo test$j
    sleep 2
    echo >&5
  }&
done
 
wait
 
exec 5>&-
exec 5<&-

我们生成做绑定时 可以用 exec 5<>tm1 来实现,
但关闭时,必须分开来写:> 读的绑定,< 标识写的绑定, <> 则标识 对文件描述符5的所有操作等同于对管道文件tm1的操作。

打开文件作为输入,并分配文件描述符
$exec 3<datafile

打开文件作为输出,并分配文件描述符
$exec 4>datafile

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢打篮球的普通人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值