5.2 KiB
5.2 KiB
Shell 并发控制
一:文件描述符
FD:文件描述符 / 文件句柄,进程使用文件描述符来管理进程打开的文件。
1. 查看当前进程打开的文件
[root@wxin ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 3月 26 17:21 0 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 1 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 2 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 19:49 255 -> /dev/pts/0
2. 自定义当前进程用描述符号操作文件
手动定义文件描述符,只要没有被占用可以使用
自定义用当前进程打开一个文件
[root@wxin ~]# exec 6<> /file
[root@wxin ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 3月 26 17:21 0 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 1 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 2 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 19:49 255 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 6 -> /file
自定义用当前进程关闭一个文件
[root@wxin ~]# exec 6<&-
[root@wxin ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 3月 26 17:21 0 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 1 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 2 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 19:49 255 -> /dev/pts/0
3. 给文件写入内容
文件描述符fd方式
[root@wxin ~]# echo "wxin" >> /proc/$$/fd/6
[root@wxin ~]# cat /file
wxin
文件名方式
[root@wxin ~]# echo "wxin" >> /file
4. 文件描述符恢复文件
删除源文件
[root@wxin ~]# rm -rf /file
新文件产生新的文件 inode
[root@wxin ~]# cp /proc/$$/fd/6 /file
查看状态为(deleted)
[root@wxin ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 3月 26 17:21 0 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 1 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 2 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 19:49 255 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 6 -> /file (deleted)
释放文件描述符,重新读取就显示正常了
二:管道
1. 匿名管道
[root@wxin ~]# rpm -qa |grep rpm
2. 命令管道
创建命名管道文件(不是普通文件,管道内的文件只能读取一次,不会永久保存)
[root@wxin ~]# mkfifo /tmp/fifo1 f
查看命名管道文件
[root@wxin ~]# cat /tmp/fifo1
[root@wxin ~]# file /tmp/fifo1
/tmp/fifo1: fifo (named pipe)
三:并发控制
1. 基础并发:后台任务与wait
通过 &
将命令放到后台执行,再通过 wait
等待所有任务完成。
#!/bin/bash
# 启动多个后台任务
wget -q http://example.com/file1.jpg &
wget -q http://example.com/file2.jpg &
wget -q http://example.com/file3.jpg &
# 等待所有后台任务完成
wait
echo "All downloads completed!"
2. 控制并发数量
同时运行过多任务可能导致资源耗尽。以下方法可限制并发数量:
方法 1:循环计数 + 等待
#!/bin/bash
max_workers=3 # 最大并发数
task_list=(task1 task2 task3 task4 task5) # 任务列表
for task in "${task_list[@]}"; do
(
# 执行任务(此处替换为实际逻辑)
echo "Processing $task..."
sleep 1
) &
# 控制并发数量
if [[ $(jobs -r | wc -l) -ge $max_workers ]]; then
wait -n # 等待任意一个后台任务完成(需要 bash 4.3+)
fi
done
wait # 等待剩余任务完成
echo "All tasks done!"
方法 2:使用 xargs
的 -P
参数
# 使用 xargs 控制并行进程数
echo task1 task2 task3 task4 task5 | xargs -n1 -P3 -I{} bash -c 'echo "Processing {}"; sleep 1'
方法 3:命名管道(FIFO)
#!/bin/bash
max_workers=3
task_list=(task1 task2 task3 task4 task5)
# 创建命名管道
fifo=$(mktemp -u)
mkfifo "$fifo"
exec 3<>"$fifo"
rm -f "$fifo"
# 初始化管道令牌
for ((i=0; i<max_workers; i++)); do
echo >&3
done
# 处理任务
for task in "${task_list[@]}"; do
read -u3 # 获取令牌
{
echo "Processing $task..."
sleep 1
echo >&3 # 归还令牌
} &
done
wait
exec 3>&-
echo "All tasks done!"
3. 示例
#!/bin/bash
max_workers=3
urls=(
"http://example.com/file1.jpg"
"http://example.com/file2.jpg"
"http://example.com/file3.jpg"
"http://example.com/file4.jpg"
)
download() {
local url=$1
local filename=$(basename "$url")
if wget -q "$url" -O "$filename"; then
echo "Downloaded: $filename"
else
echo "Failed: $filename" >&2
return 1
fi
}
# 控制并发下载
counter=0
for url in "${urls[@]}"; do
((counter++))
( download "$url" ) &
if [[ $((counter % max_workers)) -eq 0 ]]; then
wait # 每启动 max_workers 个任务后等待
fi
done
wait
echo "All downloads processed!"