shell实现控制进程并发数

本文介绍了如何利用文件描述符进行进程间的输入输出操作,区分了不同类型的文件描述符,并展示了如何通过命名管道实现进程并发数的控制。通过实例演示了创建、使用和管理文件描述符以及命名管道的工作原理。
摘要由CSDN通过智能技术生成

想要实现控制进程并发数,需要先了解下什么是文件描述符和命名管道

1. 文件描述符

介绍

文件描述符是一个非负整数,内核需要通过文件描述符来访问文件。当我们在系统中打开已有的文件或新建文件时,内核每次都会给特定的进程返回一个文件描述符,通过这个文件描述符来对文件进行读写操作。系统中内核默认会为每个进程初始创建三个标准的文件描述符,分别是0(标准输入)1(标准输出)2(标准错误)。可通过ll /proc/<pid>/fd查看指定进程所拥有的所有文件描述符

文件描述符操作

语法

创建:exec 文件描述符 <> 文件名

使用:&文件描述符

删除:exec 文件描述符<&-exec 文件描述符>&-

使用示例
# 创建仅可输入的文件描述符
exec 1000>test.txt # 创建仅可输入的文件描述符
echo hello >&1000  # 通过文件描述符写入数据
echo world >&1000
cat test.txt       # 查看是否写入成功
hello
world
cat <&1000           # 从仅输入文件描述符读取数据失败
cat: -: Bad file descriptor
exec 1000<&-         # 关闭文件描述符

# 创建仅可输出的文件描述符
touch test2.txt      # 创建文件(输入文件描述符会自动创建文件,输出文件描述符不可以)
exec 1001<test2.txt  # 创建仅可输出的文件描述符
echo hello >&1001    # 通过文件描述符写入数据报错
-bash: echo: write error: Bad file descriptor
exec 1000<&-         # 关闭文件描述符

# 创建可读写的文件描述符
exec 1003<>test.txt  # 创建可读写的文件描述符
cat <&1003           # 通过文件描述符读取数据
hello
world
echo hi >&1003       # 通过文件描述符写入数据
cat test.txt         # 查看内容
hello
world
hi
exec 1000<&-         # 关闭文件描述符

# 数据丢失案例
echo "init" > new.txt
exec 1000>new.txt    # 创建仅输入文件描述符
echo "end" >&1000    # 通过文件描述符写入数据
exec 1000<&-         # 关闭文件描述符
cat new.txt          # 验证
end

# 通过追加解决数据丢失问题
exec 1000>>new.txt  # 通过文件描述符追加数据
echo "start" >&1000
exec 1000<&-
cat new.txt
end
start
通过read读取数据

通过read -u命令可以通过文件描述符获取文件内容,不过与cat命令查看全部文件内容不同,read命令默认一次仅读取一行数据,可以通过-n选项指定读取行数。

示例:

cat test.txt         # 准备一个有数据的文件
line1
line2
line3
exec 2000<text.txt   # 创建仅输出述符
read -u2000 text     # 读取一行并赋值给text变量
echo $text           # 查看变量
line1
read -u2000 text     # 读取一行并赋值给text变量
echo $text           # 查看变量
line2
read -u2000 text     # 读取一行并赋值给text变量
echo $text           # 查看变量
line3
read -u2000 text     # 当内容读取完后如果继续读取
echo $text           # 查看变量
                     # 结果为空
cat <&2000           # 从文件描述符里读取所有数据
                     # 结果同样为空

通过以上演示可以得出,文件描述符并不是简单的对应一个文件,文件描述符中还包含有很多文件相关的信息,如权限、文件偏移量等。其中文件偏移量就像一个指针,默认指向第一行,当使用read读取一行后,指针指向下一行,以此类推,直到文件读取完毕。

其实不仅read会导致指针偏移,cat <&100同样也是,不同的是read默认一次偏移一行,cat <&100直接会导致指针指到文件末尾。除了以上方式,通过文件描述符输入数据同样会导致指针偏移。

提示:如果执行read -u2000后面不跟变量名同样会导致指针偏移

2. 命名管道

管道是进程间通信的一种方式,使用|会创建一个匿名管道,但是匿名管道只能实现父进程与子进程之间的数据交换,想要实现无关的进程间通信就需要命名管道,也叫FIFO文件(First In First Out 先进先出)。命名管道有如下几个特性

  • FIFO文件可以通过mknodmkfifo命令创建
  • 写入管道的数据一旦被读取后,就不可以再重复读取
  • 进程往命名管道中写入数据时,如果管道中没有数据,则写进程会被阻塞
  • 进程从命名管道中读取数据时,如果管道中没有数据,则读进程会被阻塞
  • 命名管道中的数据常驻内存,并不实际写入磁盘,读写效率会更高

示例:

mkfifo pipefile                 # 创建命名管道,不指定权限
mkfifo -m 664 pipefile2         # 创建命名管道,并指定权限
ls -l pipefile pipefile2        # 查看文件权限
prw-r--r-- 1 root root   0 Nov 17 11:01 pipefile
prw-rw-r-- 1 root root   0 Nov 17 11:21 pipefile2
echo "hello world" > pipefile   # 写阻塞
cat pipefile                    # 读数据,并解除写阻塞
hello world
cat pipefile                    # 读阻塞
echo "hello fifo" > pipefile    # 写数据,并解除读阻塞

3. 控制进程并发数

通过文件描述符和命名管道就能实现控制进程的并发数,实现方式如下脚本所示。

#!/bin/bash

# 创建命名管道和文件描述符
mkfifo ch3
exec 12<> ch3
# 删除ch3文件防止下次执行影响执行(删除后不影响文件描述符使用)
rm -f ch3

# 通过文件描述符往命名管道中写入5行任意数据,用于控制进程数量
for i in {1..5};do
    echo "" >&12
done

# 实现循环执行20次sleep命令,执行过程中保持同时有5个进程并发执行
# 首次并发执行5个进程后,这时的命名管道ch3中数据量是0条,会造成阻塞
# read -u12 表示每次从命名管道读取一行
# {...} & 表示括号内的一组命令后台执行
# echo "" >&12 表示当一个sleep进程执行完成后会往命名管道里添加一行新的数据
# 这时命名管道ch3中数据量是1条,会取消阻塞启动一个新的sleep进程
# 启动之后,ch3中数据量是0条,继续造成阻塞,当有新的sleep命令执行完成后,就会有新的sleep进程执行
# 循环往复,直到执行20次sleep命令

for j in {1..20};do
    read -u12 
    { 
        echo "start sleep $j"
        sleep 5
        echo "stop sleep $j"
        echo "" >&12
    } &
done
# 等待所有后台进程执行完成
wait

执行结果如下

start sleep 1
start sleep 5
start sleep 2
start sleep 3
start sleep 4
stop sleep 5
stop sleep 1
start sleep 6
start sleep 7
stop sleep 2
stop sleep 4
stop sleep 3
start sleep 8
start sleep 9
start sleep 10
stop sleep 6
start sleep 11
stop sleep 7
start sleep 12
stop sleep 8
stop sleep 9
stop sleep 10
start sleep 13
start sleep 15
start sleep 14
stop sleep 11
start sleep 16
stop sleep 12
start sleep 17
stop sleep 13
stop sleep 15
start sleep 18
start sleep 19
stop sleep 14
start sleep 20
stop sleep 16
stop sleep 17
stop sleep 19
stop sleep 18
stop sleep 20

参考链接

此文参考了丁明一大佬所著《Linux Shell核心编程指南》

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值