Linux 并发实现

来源:互联网 发布:桂林广电网络营业厅 编辑:程序博客网 时间:2024/06/05 20:57

一、非并发执行

[root@xml-ora1 ~]# cat nopara.sh

#!/bin/sh

for((i=0;i<5;i++));do

{

echo "$! start `date`.";

sleep 5;

echo 1>> aa && echo "$!done `date`.";

}

done

wait

cat aa | wc -l

rm aa

[root@xml-ora1 ~]# sh nopara.sh

 start Mon Mar 18 16:50:25 CST 2013.

 doneMon Mar 18 16:50:30 CST 2013.

 start Mon Mar 18 16:50:30 CST 2013.

 doneMon Mar 18 16:50:35 CST 2013.

 start Mon Mar 18 16:50:35 CST 2013.

 doneMon Mar 18 16:50:40 CST 2013.

 start Mon Mar 18 16:50:40 CST 2013.

 doneMon Mar 18 16:50:45 CST 2013.

 startMon Mar 18 16:50:45 CST 2013.

 doneMon Mar 18 16:50:50 CST 2013.

5

 

[root@xml-ora1 ~]# ps -ef | grep para | grep -v grep

root    19294  3092  0 16:50 pts/2    00:00:00 sh nopara.sh

二、并发执行

在bash中,使用后台任务来实现任务的“多进程化”。这个就在上面基础上多加了一个后台执行&符号,此时应该是5个循环任务并发执行。

[root@xml-ora1 ~]# cat para.sh

#!/bin/sh

for((i=0;i<5;i++));do

{

echo "$! start `date`.";

sleep 5;

echo 1>> aa && echo "$!done `date`.";

} &

done

wait

cat aa | wc -l

rm aa

注意:wait是等待前面的后台任务全部完成才往下执行,否则程序本身是不会等待的,这样对后面依赖前面任务结果的命令来说就可能出错。例如上面wc -l的命令就报错:不存在aa这个文件。

 

[root@xml-ora1 ~]# sh para.sh

19261 start Mon Mar 18 16:47:51 CST 2013.

19264 start Mon Mar 18 16:47:51 CST 2013.

19267 start Mon Mar 18 16:47:51 CST 2013.

19272 start Mon Mar 18 16:47:51 CST 2013.

19269 start Mon Mar 18 16:47:51 CST 2013.

19261 done Mon Mar 18 16:47:56 CST 2013.

19264 done Mon Mar 18 16:47:56 CST 2013.

19272 done Mon Mar 18 16:47:56 CST 2013.

19269 done Mon Mar 18 16:47:56 CST 2013.

19267 done Mon Mar 18 16:47:56 CST 2013.

5

 

在不加控制的模式下,不管有多少任务,全部都后台执行。也就是说在这种情况下,有多少任务就有多少“进程”在同时执行。在后台进程可以看到如下信息:

[root@xml-ora1 ~]# ps -ef | grep para | grep -v grep

root    19260  3092  0 16:47 pts/2    00:00:00 sh para.sh

root    19261 19260  0 16:47 pts/2    00:00:00 sh para.sh

root    19264 19260  0 16:47 pts/2    00:00:00 sh para.sh

root    19267 19260  0 16:47 pts/2    00:00:00 sh para.sh

root    19269 19260  0 16:47 pts/2    00:00:00 sh para.sh

root    19272 19260  0 16:47 pts/2    00:00:00 sh para.sh

 

三、并发执行精细控制

以上所讲的实例都是进程数目不可控制的情况,下面描述如何准确控制并发的进程数目。

脚本通过建立FIFO(命名管道),建立起进程之间的通讯,并且通过FIFO实现命令的队列化(先进先出),主进程不断地让要执行的命令在后台执行,并且通过控制并发数量,实现在后台运行的命令的个数。

(1)         FIFO使命令队列化,并且控制并发数量;

(2)         后台执行命令,父进程使用wait等待全部子进程结束;

(3)         获取失败信息,日志;

(4)         捕获信号,进行信号处理;

 

[root@xml-ora1 ~]# cat paractl.sh

#!/usr/bin/bash

 

#定义并发进程数量

PARALLEL=3

#定义临时管道文件名($$.是代表Pid特殊变量)

TMPFILE=$$.fifo

#定义导出配置文件全路径名

CMD_CFG=$HOME/ptest.cfg

#定义失败日志文件

FAILURE_FLAG=$HOME/fail.log

 

# 中断时kill子进程(kill -9 0:杀死脚本自己及衍生出来的子进程)

function trap_exit

{

       kill -9 0

}

 

#执行命令函数

exec_cmd()

{

# 此处为实际需要执行的命令,本例中用sleep做示例

       echo "`date` seq:${SEC} $! start!"

       sleep ${1}

       echo "`date` seq:${SEC} $! complete!"

echo

       if [ $? -ne 0 ]

       then

                echo "cmd run fail!"

                return 1

       fi

}

 

#当信号为12315时,执行trap中的命令,即调用trap_exit函数,并且退出该shell和返回信号2——01留给后面用

trap 'trap_exit; exit 2' 1 2 3 15

#清理失败标识文件

rm -f ${FAILURE_FLAG}

#为并发进程创建相应个数的占位(创建命名管道)

mkfifo $TMPFILE

#为命名管道指定文件标识符为4<>分别是输入和输出,即绑定了该管道的输入输出都在4这个文件标识符上!

exec  4<>$TMPFILE

 

rm -f $TMPFILE

 

#FIFO写入$PARALLEL个数的换行,每个回车代表一个并发占位(用{}和用()的区别在shell是否会衍生子进程。let命令用以变量运算。)

{

       count=$PARALLEL

       while [ $count -gt 0 ]

       do

                echo “conitue! $count”

                let count=$count-1

      done

} >&4

 

#从任务列表seq中按次序获取每一个任务,Home目录下的cfg文件中读取sleep的时间,即代表需要执行的任务数。

while read SEC

do read<&4

#显示执行任务号生成的子ID的进程号

echo "`date` seq:${SEC} pid:$$ "

#后台执行主程序命令或者输出错误日志,完成后清空标识符

(exec_cmd ${SEC} || echo${SEC}>>${FAILURE_FLAG} ; echo >&4 ) &

done<$CMD_CFG

#等待子进程结果返回值

wait

exec 4>&-

#并发进程结束后判断是否全部成功

if [ -f ${FAILURE_FLAG} ]

then

       exit 1

else

       exit 0

fi

 

配置文件:表示需要程序睡眠的时间,其实就是多少个任务需要被执行。这里有3个任务,5代表第一个任务睡眠5秒,10睡眠10秒,13睡眠13秒。

[root@xml-ora1 ~]# cat ptest.cfg

5

10

13

 

                先将并行脚本paractl.sh里的定义并行参数PARALLEL=3改为PARALLEL=1看看依次执行情况:

[root@xml-ora1 ~]# sh paractl.sh

Mon Mar 18 19:30:50 CST 2013 seq:5 20450start!

Mon Mar 18 19:30:55 CST 2013 seq:5 20450complete!

 

Mon Mar 18 19:30:55 CST 2013 seq:10 20454start!

Mon Mar 18 19:31:05 CST 2013 seq:10 20454complete!

 

Mon Mar 18 19:31:05 CST 2013 seq:13 20460start!

Mon Mar 18 19:31:18 CST 2013 seq:13 20460complete!

 

再将并行脚本paractl.sh里的定义并行参数PARALLEL=1改为PARALLEL=2看看执行情况:

[root@xml-ora1 ~]# sh paractl.sh

Mon Mar 18 19:42:07 CST 2013 seq:5 20578 start!

Mon Mar 18 19:42:07 CST 2013 seq:10 20581start!

Mon Mar 18 19:42:12 CST 2013 seq:5 20578complete!

 

Mon Mar 18 19:42:12 CST 2013 seq:13 20585 start!

Mon Mar 18 19:42:17 CST 2013 seq:10 20581complete!

 

Mon Mar 18 19:42:25 CST 2013 seq:13 20585complete!

 

                很清晰的展现了,并发数量为1的时候执行的情况,先执行前2个任务,5秒后任务5执行完后,在启动13这个任务,改回PARALLEL=3后的3个任务并行执行情况如下:

 

[root@xml-ora1 ~]# sh paractl.sh

Mon Mar 18 19:33:25 CST 2013 seq:5 20471 start!

Mon Mar 18 19:33:25 CST 2013 seq:10 20474 start!

Mon Mar 18 19:33:25 CST 2013 seq:13 20477 start!

Mon Mar 18 19:33:30 CST 2013 seq:5 20471 complete!

Mon Mar 18 19:33:35 CST 2013 seq:10 20474 complete!

Mon Mar 18 19:33:38 CST 2013 seq:13 20477 complete!

 

很明显3个任务同时执行。

四、并发应用

                为什么要研究linux的shell并发呢,因为我想测试下我的邮件服务器的并发处理性能如何,会不会有性能上的问题,之前帮同事做了个sendmail的搭建和对转发的控制的mliter的开发,功能完成,但是毕竟我还是个dba,而不是专职做linux c开发的,只是花了2周改了改mliter的示例代码。稳定重于一切。

1、自动发送邮件脚本

[root@xml-ora1 ~]# cat telnet.sh

#!/usr/bin/expect

set smtp   [lindex $argv 0]

set from   [lindex $argv 1]

set to     [lindex $argv 2]

set title  [lindex $argv 3]

set content [lindex $argv 4]

spawn telnet $smtp 25

expect "220"

send "HELO xinhua.org\r"

expect "250 OK"

send "MAIL FROM: <$from>\r"

expect "250 Mail OK"

send "RCPT TO: <$to>\r"

expect "250 Mail OK"

send "DATA\r"

expect "354"

send "TO: $to\r"

send "FROM: $from\r"

send "SUBJECT: $title\r"

send "\r"

send "$content\r"

send ".\r"

expect "250"

send "QUIT"

 

2、并发发送邮件脚本

[root@xml-ora1 ~]# cat parasend.sh

#!/bin/sh

for((i=0;i<80;i++));do

{

echo "$i send start `date`pid:$!.";

 

echo `./telnet.sh 172.16.28.20  $i@126.com java3344520@hotmail.com subjects$i content`

 

echo `./telnet.sh 172.16.28.20  410566413@qq.com  java3344520@hotmail.com subjects$i content$i`

 

echo `./telnet.sh 172.16.28.20  $i@126.com java3344520@hotmail.com subjects$i content`

 

echo `./telnet.sh 172.16.28.20  $i@qq.com a@xxx.com subjects$i content$i`

 

echo 1>> aa && echo "$isend complete `date` pid:$!.";

} &

done

wait

cat aa | wc -l

rm aa

3、并发发送邮件结果

单台转发-批量执行脚本

                单台服务器上并发数量情况下:

并发数量

邮件处理

50

正常

80

正常

100

失败

 

                因为我们的环境是单台服务器的转发,所以并发数量为80个数量左右,而邮件服务器,正常是由队列发出的,邮件服务器domino需要控制并发发送的数量。

多台转发-批量执行脚本

为了测试,在2台客户端机器上执行该脚本,在2台客户端上上执行,如果循环50次的话,会出现内存问题,而退出过滤条件,而稳定在30个循环,即并发数量在每秒50个邮件左右。后续需要改进程序,加强效率。

多台服务器上并发数量情况下:

并发数量

邮件处理

20

正常

30

正常

50

失败

 

4、结论

                需要根据邮件服务器的情况得到转发服务器过滤插件的性能,需要上线进行实际测试。

外行人的编程水平啊,满足需求即可!