开篇
近期在给一个客户编写数据库迁移工具,语言使用的是不太熟悉的perl。而需要做进程间通信源自这样一个需求,即并行迁移,想要真正的提升性能,我没有选择多线程的方式,而是直接选择多进程。
而我们都知道,多进程和多线程的区别就在于多进程的稳定性,多进程的内存资源是独立的,而多线程确实和父进程共享的。场景图示如下,
描述一下,首先通过简单的算法,将表大致平均地分到多个子进程,每个子进程任务完成后统计成功的表数量。那么统计表数量的变量就是关键了,在这里多个进程是需要共享这个变量的
linux下的进程间通信,共有三个媒介,消息队列、共享内存段以及信号量。通常共享内存段和信号量配合使用,而这里我们只需要做简单的 持锁+计数+释锁,这么说来,此场景下信号量就是个天然的计数器了。
这三种媒介可以使用ipcs命令查看,
使用
如下代码示例,
#!/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命令查看信号量集。
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]#
然后查看信号量,发现已经被销毁了
那么现在再执行,信号量就应该是重新创建了,
[root@mysqltest2 testconfig]# perl sem.pl
5
[root@mysqltest2 testconfig]#
执行结果完全符合。最后两次的执行首先基于旧的信号量做操作,因此执行完毕时信号量值为20,然后销毁掉信号量,下次执行重新创建,因此最后一次的值为5。
结语
只是几句简单的代码,却帮了我的大忙,理解至上。