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

首先我把for循环中的代码用{}包为一个块,然后增加&符号使其后台运行,之后增加wait指令,该指令会等待所有后台进程结束,如果不加wait,程序直接往下走,最终打出的time将会是0。现在程序已经由之前的10秒缩短为2秒,似乎效果不错,不过试想这样一个场景,有1000个这样的任务,如果还是以这种方式执行,机器负载是扛不住的,我们必须想一种办法来控制进程的并发数,那就是管道和文件描述符。
首先介绍下管道(pipe):

  • 无名管道
    它经常出现在我们的命令中,例如cat /etc/passwd | awk -F: ‘{print $1}’,其中"|"就是管道,它将前一个命令的结果输出到后一个进程中,作为两个进程的数据通道,不过他是无名的。
  • 有名管道
    使用mkfifo命令创建的管道即为有名管道,例如,mkfifo pipefile, pipefile即为有名管道。

管道有一个显著的特点,如果管道里没有数据,那么去取管道数据时,程序会阻塞住,直到管道内进入数据,然后读取才会终止这个操作,反之,管道在执行写入操作时,如果没有读取操作,同样会阻塞。

#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>&- 
exex 8<&-

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

echo "finish"

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
匿名管道命名管道都是用于进程间通信的方式,但它们之间有一些区别。 匿名管道: 匿名管道是一种单向通信方式,只能用于父子进程或者兄弟进程之间的通信。在创建匿名管道时,操作系统会自动为其分配一个文件描述符,通过该文件描述符,进程可以进行读写操作。 匿名管道的创建方式如下: ```C int pfd[2]; pipe(pfd); // 创建匿名管道 ``` 其中,pfd[0]是管道的读端,pfd[1]是管道的写端。 匿名管道的使用方式如下: ```C char buf[1024]; pid_t pid; int pfd[2]; pipe(pfd); pid = fork(); if (pid == 0) { // 子进程 close(pfd[1]); // 关闭写端 read(pfd[0], buf, sizeof(buf)); // 读取数据 printf("child process read from pipe: %s\n", buf); close(pfd[0]); // 关闭读端 } else if (pid > 0) { // 父进程 close(pfd[0]); // 关闭读端 write(pfd[1], "hello world", strlen("hello world")); // 写入数据 close(pfd[1]); // 关闭写端 } else { perror("fork"); exit(1); } ``` 命名管道命名管道也是一种单向通信方式,但可以用于任意进程之间的通信。在创建命名管道时,需要指定一个路径名,并且需要手动创建该文件。操作系统会为其分配一个文件描述符,通过该文件描述符,进程可以进行读写操作。 命名管道的创建方式如下: ```C int fd; mkfifo("/tmp/myfifo", 0666); // 创建命名管道 fd = open("/tmp/myfifo", O_RDONLY); // 打开命名管道 ``` 其中,"/tmp/myfifo"是文件路径名,0666是文件权限。 命名管道的使用方式如下: ```C char buf[1024]; int fd; fd = open("/tmp/myfifo", O_RDONLY); read(fd, buf, sizeof(buf)); // 读取数据 printf("read from fifo: %s\n", buf); close(fd); ``` 需要注意的是,命名管道的写入操作可以在任意进程中进行,只要有权限打开该文件即可。如果多个进程同时写入数据到同一个命名管道,可能会导致数据混乱。因此,使用命名管道时需要特别注意数据的同步问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值