perl信号量实现进程间通信

开篇

  近期在给一个客户编写数据库迁移工具,语言使用的是不太熟悉的perl。而需要做进程间通信源自这样一个需求,即并行迁移,想要真正的提升性能,我没有选择多线程的方式,而是直接选择多进程。
  而我们都知道,多进程和多线程的区别就在于多进程的稳定性,多进程的内存资源是独立的,而多线程确实和父进程共享的。场景图示如下,
perl信号量实现进程间通信
  描述一下,首先通过简单的算法,将表大致平均地分到多个子进程,每个子进程任务完成后统计成功的表数量。那么统计表数量的变量就是关键了,在这里多个进程是需要共享这个变量的

  linux下的进程间通信,共有三个媒介,消息队列、共享内存段以及信号量。通常共享内存段和信号量配合使用,而这里我们只需要做简单的 持锁+计数+释锁,这么说来,此场景下信号量就是个天然的计数器了。
  这三种媒介可以使用ipcs命令查看,
perl信号量实现进程间通信

使用

如下代码示例,

#!/usr/bin/perl
use IPC::SysV qw(S_IRWXU IPC_CREAT);
use IPC::Semaphore;

my $sem0;
my $sem1;
my @childs;

my $sem = IPC::Semaphore->new(1556, 1, S_IRWXU|IPC_CREAT)
        || die "IPC::Semaphore->new: $!\n";
##创建一个信号量集,1556是key,1是信号量的个数,S_IRWXU即700权限,IPC_CREAT没有则创建
$sem->setval(0,0);
##设置初始值,这里只有一个信号量则下标为0,初始值为0
for ($i=0; $i<5; $i++){
                if($pid=fork())
                        {
                        $childs[$i] = $pid;
                }elsif(defined $pid){
                        sleep(1);
                        $sem->op(0, +1, SEM_UNDO);
##op方法通过semop来调用系统PV原子操作,子进程退出时会通过 SEM_UNDO 来解锁
                        exit;
                }else{
                        print "Error!\n";
                        exit;
                }
}
        for  $p (@childs){
                waitpid($p, 0);
        }

$sem1 = $sem->getval(0);
##获取信号量值
print $sem1."\n";

其中涉及到的几个常用方法如下,

new ( KEY , NSEMS , FLAGS )
Create a new semaphore set associated with KEY . NSEMS is the number of semaphores in the set. A new set is created if

● KEY is equal to IPC_PRIVATE
● KEY does not already have a semaphore identifier associated with it, and FLAGS & IPC_CREAT is true.
On creation of a new semaphore set FLAGS is used to set the permissions. Be careful not to set any flags that the Sys V IPC implementation does not allow: in some systems setting execute bits makes the operations fail.

setval ( N , VALUE )
Set the N th value in the semaphore set to VALUE

op ( OPLIST )
OPLIST is a list of operations to pass to semop. OPLIST is a concatenation of smaller lists, each which has three values. The first is the semaphore number, the second is the operation and the last is a flags value.

getval ( SEM )
Returns the current value of the semaphore SEM .

输出结果如下

[root@mysqltest2 testconfig]# perl sem.pl 
5
[root@mysqltest2 testconfig]# 

销毁

执行完成之后,再使用ipcs命令查看信号量集。
perl信号量实现进程间通信
  614转换成十进制数,即2566+161+4=1556,并且权限是700,这个信号量是不是很熟悉呢?

由于使用了IPC_CREAT这个flag,因此我们注释掉初始化操作,再执行一下程序看看。如下

my $sem = IPC::Semaphore->new(1556, 1, S_IRWXU|IPC_CREAT)
        || die "IPC::Semaphore->new: $!\n";
#$sem->setval(0,0);

执行结果如下,

[root@mysqltest2 testconfig]# perl sem.pl 
10
[root@mysqltest2 testconfig]#

  从输出结果我们看到,由于信号量一直存在因此没有重新创建,PV操作也是基于上一次的结果继续进行的。

再次执行,

[root@mysqltest2 testconfig]# perl sem.pl 
15
[root@mysqltest2 testconfig]# 

remove方法。官方解释如下,

remove

  Remove and destroy the semaphore set from the system.

那么意义很明显,就是用来销毁信号量的。我们在代码尾部添加销毁语句,

$sem->remove();

再次执行结果如下,

[root@mysqltest2 testconfig]# perl sem.pl 
20
[root@mysqltest2 testconfig]#

然后查看信号量,发现已经被销毁了
perl信号量实现进程间通信
那么现在再执行,信号量就应该是重新创建了,

[root@mysqltest2 testconfig]# perl sem.pl 
5
[root@mysqltest2 testconfig]#

  执行结果完全符合。最后两次的执行首先基于旧的信号量做操作,因此执行完毕时信号量值为20,然后销毁掉信号量,下次执行重新创建,因此最后一次的值为5。

结语

  只是几句简单的代码,却帮了我的大忙,理解至上。

上一篇:pkm的linq to Entities学习2


下一篇:银行存取款模型的线程同步问题