Shell 并发控制

------ ## 一:文件描述符 ​ FD:文件描述符 / 文件句柄,进程使用文件描述符来管理进程打开的文件。 ### 1. 查看当前进程打开的文件 ```bash [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. 自定义当前进程用描述符号操作文件 手动定义文件描述符,只要没有被占用可以使用 自定义用当前进程打开一个文件 ```bash [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 ``` 自定义用当前进程关闭一个文件 ```bash [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方式 ```bash [root@wxin ~]# echo "wxin" >> /proc/$$/fd/6 [root@wxin ~]# cat /file wxin ``` 文件名方式 ```bash [root@wxin ~]# echo "wxin" >> /file ``` ### 4. 文件描述符恢复文件 删除源文件 ```bash [root@wxin ~]# rm -rf /file ``` 新文件产生新的文件 inode ```shell [root@wxin ~]# cp /proc/$$/fd/6 /file ``` 查看状态为(deleted) ```bash [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. 匿名管道 ```bash [root@wxin ~]# rpm -qa |grep rpm ``` ### 2. 命令管道 创建命名管道文件(不是普通文件,管道内的文件只能读取一次,不会永久保存) ```bash [root@wxin ~]# mkfifo /tmp/fifo1 f ``` 查看命名管道文件 ```bash [root@wxin ~]# cat /tmp/fifo1 [root@wxin ~]# file /tmp/fifo1 /tmp/fifo1: fifo (named pipe) ``` ## 三:并发控制 ### 1. 基础并发:后台任务与`wait` 通过 `&` 将命令放到后台执行,再通过 `wait` 等待所有任务完成。 ```shell #!/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:循环计数 + 等待** ```shell #!/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` 参数** ```shell # 使用 xargs 控制并行进程数 echo task1 task2 task3 task4 task5 | xargs -n1 -P3 -I{} bash -c 'echo "Processing {}"; sleep 1' ``` **方法 3:命名管道(FIFO)** ```shell #!/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&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. 示例 ```shell #!/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!" ```