Perl进程间数据共享

Perl进程间数据共享,第1张

概述本文介绍的Perl进程数据共享内容主体来自于《Pro Perl》的第21章。 IPC简介 通过fork创建多个子进程时,进程间的数据共享是个大问题,要么建立一个进程间通信的通道,要么找到一个两进程都引用的共享变量。本文将介绍Unix IPC的近亲System V IPC:message queues(消息队列)、semaphores(信号量)和shared memory-segments(共享内

本文介绍的Perl进程间数据共享内容主体来自于《Pro Perl》的第21章。

IPC简介

通过fork创建多个子进程时,进程间的数据共享是个大问题,要么建立一个进程间通信的通道,要么找到一个两进程都引用的共享变量。本文将介绍Unix IPC的近亲System V IPC:message queues(消息队列)、semaphores(信号量)和shared memory-segments(共享内存段)。它们都是IPC结构,它们被非常广泛地应用于进程间通信。它们的帮助文档可参见:

$ perldoc IPC::Msg$ perldoc IPC::Semaphore$ perldoc IPC::SharedMem

但是,并非所有 *** 作系统都支持System V IPC,对于那些不遵守POSIX规范的平台就不支持。当然,也并非一定要在Unix *** 作系统上才能使用IPC,只要 *** 作系统支持IPC就可以,而且就算是Unix系统上也并非一定支持IPC,可以使用ipcs命令来查看是否支持:

$ ipcs------ Message Queues --------key        msqID      owner      perms      used-bytes   messages------ Shared Memory Segments --------key        shmID      owner      perms      bytes      nattch     status------ Semaphore Arrays --------key        semID      owner      perms      nsems

message queues、semaphores和shared memory segments的共同点在于它们的数据都持久存在于内存中,且只要知道资源的ID且有权限访问,就可以被任意一个进程(所以随意多个进程)访问到。由于数据在内存中持久,且数据资源进行了ID标识,这可以使得程序退出前保存状态,然后重启时再次获取到原来的状态。

严格地说,Perl支持的IPC在于可以调用一些IPC函数:msgctl、msgget、msgrcv、msgsnd、semctl、semget、semop、shmctl、shmget、shmread、shmwrite。它们几乎是对C对应函数的封装,非常底层。尽管这些函数的文档非常丰富,但这些函数并不容易使用,IPC::家族的模块提供了面向对象的IPC功能支持。

IPC::SysV模块

要使用IPC的一些函数,常常需要导入一些IPC::SysV模块中的常量,因此很多使用IPC的程序中,都会:

use IPC::SysV:

它里面定义了很多常量,完整的可参见perldoc -m IPC::SysV,下面是一些常见的常量:

 

Message Queue(消息队列)

曾经消息队列是进程间通信的唯一有效方式,它就像管道一样,一端写入一端读取。对于消息队列而言,我们写入和读取的数据都称之为消息(message)。

可以创建两种类型的消息队列:私有的(private)和公有的(public)

私有队列只对创建它的进程和它的子进程可以访问,当然还可以通过权限控制的方式来改变访问权限
公有队列只有有权限,且知道资源ID的进程可以访问

例如,创建一个私有的消息队列。

use IPC::SysV qw(IPC_PRIVATE IPC_CREAT S_IRWXU);use IPC::Msg;my $queue = IPC::Msg->new IPC_PRIVATE S_IRWXU | IPC_CREAT;

IPC_Msg的构造函数new有两个参数,第一个是要创建的消息队列的资源ID,在IPC SysV中常称为KEY,对于私有队列来说,KEY需要指定为IPC_PRIVATE,第二个参数是访问该队列的权限,S_IRWXU表示队列的所有者(U)可以对该队列进行读(R)、写(W)、执行(X) *** 作。此处还配合了IPC_CREAT,表示如果队列不存在就创建新队列。

权限部分也可以写成数值格式的:

my $queue = IPC::Msg->new IPC_PRIVATE 0700 | IPC_CREAT;

所以,这里创建的私有队列只有创建者进程和子进程可以执行读写执行的 *** 作。

如果想要创建一个公有队列,需要为该公有队列提供一个资源ID,资源ID是一个数值。下例中给的资源ID是10023,权限是0722,表示创建队列的进程拥有读写执行 *** 作,而资源所在组或其它用户进程只能写队列。

my $q = IPC::Msg->new 10023,0722 | IPC_CREAT;

如果其它进程想要访问这个公有队列,只需通过new方法指定这个公有队列的KEY即可即表示构建这个已有的队列,不要指定IPC_CREAT修饰符,否则表示创建动作(尽管IPC结构存在时不会创建,但他代表了创建这个动作,而非访问动作)。如果要获取的公有队列不存在,则返回undef。如下:

my $q = IPC::Msg->new 10023,0200;

而对于私有队列,想要知道它的KEY,可以使用ID()方法:

$KEY = $queue->ID
发送和接收消息队列

有了消息队列的对象结构之后,就可以 *** 作这个消息队列,比如发送消息,接收消息等。相关文档参见man msgsnd

向队列发送消息和从队列中接收消息的方式为:

$queue->snd($type,$wr_msg,[ $flags ]);$queue->rcv($rd_msg,$length,$type,[ $flags ]);
$wr_msg为想要发送的消息
$rd_msg是从消息队列中读取消息保存到哪个标量变量中
$type是一个正整数,表示消息队列的类型,可在rcv方法中指定这个正整数表示选择接收哪个数值类型的消息
$length表示消息队列中最多允许接收多少条消息。如果消息长度大于该值,则rcv返回undef,并且$!设置为E2BIG
$flags是可选的,如果设置为IPC_NowAIT,则这两个方法不会阻塞,而是立即返回($!设置为EAGAIN)

关于rcv type和flag的规则,参考如下解释。

rcv Type的解释:整数值             意义----------------------------- 0           rcv总是读取队列的第一条消息,无视type >0          rcv总是读取该类型的第一条消息。例如,             type=2,则只读取type=2的消息,如果不存在,             则一直阻塞直到有type=2的消息。但是可以设             置IPC_NowAIT和MSG_EXCEPT常量改变这种模式 <0          rcv读取类型不大于type绝对值(从小到大)的第             一条消息。不严谨,但可看示例描述:如果rcv的             type=-2,则首先读取type=0的第一条消息,如             果不存在type=0的消息,则继续读取type=1的第             一条消息,不存在则继续读取type=2的第一条消息
flag的解释:flag值              意义------------------------------MSG_EXCEPT         rcv读取第一条非type值的消息。例如,rcv                   的type=1,则读取第一条type不为1的消息MSG_NOERROR        允许消息过长超过$length时截断超出的部分,                   而不是在这种情况下返回E2BIG错误  IPC_NowAIT         rcv在请求的消息类型不存在时不要阻塞等待,                   而是立即返回,且设置$!的值为EAGAIN

将上面的解释合并起来,很明确的意思是我们可以通过设置不同的type来实现多级通信的消息队列,这一切都交给我们自己来决定,例如对不同子进程或线程发送不同的消息。

获取和设置消息队列的属性

可以使用set方法来修改消息队列的权限,它需要一个key-value格式的参数。

例如:

$queue->set(    uID => $user_ID,# chown    gID => $group_ID,# chgrp    mode => $perm,# 8进制权限位或S_格式的权限    qbytes => $queue_size,# 队列最大容量(capacity));

另外,可以使用stat方法获取队列的属性对象,通过这个属性对象,可以直接修改队列的对应属性。只是需要注意的是,当通过stat对象更改属性时,不会立即应用到消息队列上生效,只有通过set方法设置后,设置才会立即生效。

my $stat = $queue->stat;$stat->mode(0722);$queue->set($stat);

最后,如果拥有队列的执行权限,可以通过remove方法销毁这个队列:

$queue->remove;

如果无法删除队列,则remove返回undef,并设置$!。其实删除队列挺重要的,因为如果程序退出,队列可能会继续保留在内存中(前文已经说过了,IPC对象都是持久化在内存中的)。

信号量和共享内存

待续

总结

以上是内存溢出为你收集整理的Perl进程间数据共享全部内容,希望文章能够帮你解决Perl进程间数据共享所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1271845.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存