shell-多进程及绑定

我今天学了些啥?nothing!没精神,好像又到了一轮低迷期。上一次是八月份吧,成天就想着生活的意义是啥。。。。。。

伪多进程

前几天写了个shell多进程,自我感觉良好。觉得不过如此吗?但是今天好好看了一下,实际上是伪多进程,因为在程序运行时用ps查看了一下,根本就没达到预期的进程数。而且从时间上也可以看出,正常跑下来需要三分钟,开了八个进程结果还是需要一份半。

所以分析了一下,是短板效应吧。最慢的进程拉低了总的效率,所以说是伪多进程了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 大致是这么个结构
# 接受一个进程数
process=$1

# 大的循环体
cat somefile | while read line
do
# 执行一个功能并放入后台
{
somefunction
} &
# 判断如果达到进程数,就等待。等所有都执行完后,计数重置,进入新的循环
if [[ ${i:-1} -eq ${process} ]];then
wait
i=0
fi
((i++))
done

wait等待所有

这里面wait的作用是等待所有的后台程序都执行完,才会继续执行下面的语句。从而达到多进程的目的,不至于无法控制进程数。

但是这么写就有个问题,那就是放入后台的执行速度并不同,实际情况是大多数都已经执行完了,但是还有一个两个速度很慢,那么只能继续等待。这就是为什么达不到预期的进程数的原因。


真多进程

我想要达到的效果是:每时每刻都有预期的进程数在后台执行,先执行完的继续执行下一个,只要保证总的进程数一定就行,有一个动态调整过程。

所以就用到了以前觉得没啥作用的管道文件。常常使用的都是匿名管道(|),不怎么常用的就是管道文件了,我也就拿来做了个terminal交互的功能(用一个terminal给另一个发送指令),没想到还可以用到多进程中。

管道嘛,有了输入,会一直等待输出,直到输出成功为止。有了输出的接收端,就会一直等待输入,直到输入数据为止。正是利用了管道输入输出阻塞的这个特性,避免了使用wait带来的全部等待。先创建进程数那么多的管道,等待输出,每循环一次,接受一个输入,将数据读出,放入后台,在这个执行完后,再生成一个管道输入,管道数为定值,所以循环想要开始就得有管道的输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 接受一个进程数
process=$1

# 接受ctrl+c的中断
trap "exec 6<>&-;exit 0" 2
# 生成管道文件
tmpfifo=/tmp/$$.fifo
mkfifo $tmpfifo

# 重新绑定管道输入输出为6。这个数字不能为0,1,2。因为0,1,2已经被系统占用了,分别为标准输入,标准输出,错误输出
exec 6<>${tmpfifo}
rm -rf ${tmpfifo}

# 生成进程数那么多的管道输入并等待输出
for (( i=i;i<=${process};i++ ))
do
echo >&6
done

# 大的循环体
cat somefile | while read line
do
# 读取一个输入,并放入后台
read -u6
{
somefunction
# 执行完后再次生成一个管道
echo >&6
} &
done
# 等待所有进程结束
wait
# 解除绑定,关闭6描述符
exec 6>&-

这里我搞不懂为什么要把fifo文件删除掉,删除了为什么不影响绑定?

这样运行之后,使用ps查看确实是保持着预期的进程数,而且在开了八个进程后时间由三分多钟缩短为20多秒。so it is true multi process

重定向

1
2
3
4
5
6
7
8
>/dev/null			#标准输出写入/dev/null
>/dev/null 2>&1 #标准错误重定向标准输出,标准输出再写入/dev/null
>& /dev/null #上面的简写

n<&- #将 n 号输入关闭
<&- #关闭标准输入(键盘)
n>&- #将 n 号输出关闭
>&- #将标准输出关闭

上面的输入,输出重定向,只对当前那条指令是有效的。如果需要在绑定之后,接下来的所有命令都支持的话。就需要用exec命令永久绑定。


exec绑定

1
exec 文件描述符[n] <或> file或文件描述符或设备
1
2
3
4
5
# 例如:将标准输出绑定到文件
exec 1>test.txt
# 绑定后在终端输入命令就没有输出了,输出全部重定向到了test.txt文件中
# 恢复,将1绑定到2.因为2和1一样都是将信息输出到屏幕
exec 1>&2

利用这个特性,可以做许多很多奇特的事,比如我们在脚本中需要将许多输出写入一个文件,之前不是需要在这么写吗echo "something" >/log/somelogfile,grep 'someword' somefile >/log/somelogfile。是不是有点麻烦,现在可以把标准输出重定向到文件中,再也不需要写重定向了,标准输出自动重定向,很神奇。

1
2
3
4
5
6
7
8
9
eg:
exec 1>/log/somelogfile
# 这样不需要再写重定向了,它会自动重定向的
echo "hello joyce"
echo "i like iu"
ls -l | awk '{print $3}'
...
# 最后再恢复就ok了
exec 1>&2