免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3849 | 回复: 6
打印 上一主题 下一主题

继续Perl文档翻译计划:Perl IPC (一) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-05-06 16:46 |只看该作者 |倒序浏览
名称

perlipc - Perl的进程间通讯(信号、fifos、管道、安全子进程、套接字与信号量)



描述

Perl的基本的进程间通讯方法有旧式UNIX信号、命名管道、打开的管道、伯克利套接字以及SysV IPC调用。每一种都在不同的情形下有各自的应用。



信号

Perl使用一种简单的信号处理机制:哈希%SIG包含了信号的名字和用户安装的信号处理句柄。这些句柄将带着触发它的信号的名字为参数被调用。一 个信号可能通常地从其它进程发送过来的一串键盘序列(如Control-C或者Control-Z)触发,或者自动地从相应的内核事件触发,比如一个子进 程退出,你的进程超过了堆栈空间,或者达到了文件大小限制。

比如,为了捕获一个交互地的信号,像这样安装一个信号处理句柄:

sub catch_zap {
my $signame = shift;
$shucks++;
die "Somebody sent me a SIG$signame";
}
$SIG{INT} = 'cat_zap'; # 可能失败噢
$SIG{INT} = \&catch_zap; # 更好的策略

在Perl 5.7.3以前,在你的处理句柄中尽可能少的操作是必要的:你注意一下我们所做的所有操作,就是设置一下全局变量然后触发一个异常。那是因为,在很多系统里,库是不可重入的:尤其内存申请及IO处理不是。那意味着,在你的处理回调句柄里做任何事情,都可能触发一个内存错误或者接下来的崩溃-看安全信号。

信号名字可以在你的系统上由命令kill -l列出,或者从Config模块获取它们。设置一个以数字为索引的@signame列表来得到名字,一个以名字为索引的%signo哈希数组来得到数字。

use Config;
defined $Config{sig_name} || die "No sigs?";
foreach $name (split(' ', $Config{sig_name})) {
$signo{$name} = $i;
$signame[$i] = $name;
$i++;
}

为了检查信号17与SIGALARM是否一样,这样做:

print "signal #17 = $signame[17]\n";
if ($signo{ALRM}) {
print "SIGALRM is $signo{ALRM}\n";
}

你也可以选择字符串'IGNORE'或者'DEFAULT'做为句柄,这样,Perl将尝试忽略信号或者做默认处理。

在很多UNIX平台上,CHLD(有时为CLD)信号被赋与'IGNORE'值时有特别地行为。在这类系统上,设置$SIG{CHLD}为'IGNORE'具有当父进程wait()它的子进程失败时不创建僵尸进程的效果(或者说,子进程被自动地回收)。在这样的系统上,设置$SIG{CHLD}为'IGNORE'的进程调用wait()通常返回-1。

一些信号可以既不被捕获也不被忽略,比如KILL和STOP(但不是TSTP)信号。为了暂时忽略信号,一种策略是使用local()语句,这可以使得你的块结束的时候,信号被恢复。(记住:local()变量是会被块内调用的函数继承的。)

sub precious {
local $SIG{INT} = 'IGNORE';
&more_functions;
}
sub more_functions {
# 中断仍然被忽略着呢……
}

发送一个信号给一个负的进程ID意味着你发送这个信号给整个Unix进程组。这块代码发送hang-up信号给自己所在的进程组的所有进程(设置$SIG{HUP}为IGNORE,所以它不会杀掉自身):

{
local $SIG{HUP} = 'IGNORE';
kill HUP => -$$;
# 比用:kill('HUP', -$$)更优美的书写
}

另一个有趣的信号是数字0。这不会对子进程有任何操作,但是会检查它是否还存活着或者它的进程UID改变过。

unless (kill 0 => $kid_pid) {
warn "something wicked happened to $kid_pid";
}

当直接在一个UID不是发送信号的UID的进程内发送信号0时会失败。因为你没有权限给它发信号,尽管这个进程还活着。你可以可以通过%!判断失败的原因。

unless (kill 0 => $pid or $!{EPERM}) {
warn "$pid looks dead";
}

你也可能希望对简单的信号处理句柄注册匿名函数:

$SIG{INT} = sub { die "\nOutta here!\n" };

但是对于用来重新注册它们的更加复杂的句柄将会有问题。因为Perl的信号机制,是基于C库的signal(3)函数,你在一些系统上可能有时会不幸地失败,即,它的行为是老的SysV式的而不是新的、更加合理的BSD和POSIX方式的。所以,你有时看到保守的人们像这样书写信号句柄:

sub REAPER {
$waitedpid = wait;
# 恶心的sysV: 它使得我们不能恢复
# 这个信号, 但是把它放在wait后面。
$SIG{CHLD} = \&REAPER;
}
$SIG{CHLD} = \&REAPER;
# 现在开始做创建后的事情……

或者更加好:

use POSIX ":sys_wait_h";
sub REAPER {
my $child;
# 如果第1个子进程死亡的引起的信号处理,导致了
# 第2个子进程的结束,我们不会收到第2个信号。所以我们必须循环,或者
# 留下这个子进程做为一个僵尸进程。于是下一次
# 2个子进程死亡我们又得了一个僵尸进程。等等等等。 while (($child = waitpid(-1,WNOHANG)) > 0) {
$Kid_Status{$child} = $?;
}
$SIG{CHLD} = \&REAPER; # 仍然是讨厌的sysV
}
$SIG{CHLD} = \&REAPER;
# 开始做创建后的事情……

信号处理也用在Unix的超时操作中,比如,你在一个被安全保护的eval{}块中设置一个信号来捕获超时信号来实现一些时间后调度特定的操作。 然后你阻塞你的操作,在你从你的eval{}块中退出前清除超时信号。如果它失控了,你将使用die()来跳出你的块,就像你在其它语言中使用longjmp()或者throw()。

看一个例子:

eval {
local $SIG{ALRM} = sub { die "alarm clock restart" };
alarm 10;
flock(FH, 2); # blocking write lock
alarm 0;
};
if ($@ and $@ !~ /alarm clock restart/) { die }

如果这个超时了的操作是system()或者qx(),这个技巧可以避免创建僵尸进程。如果这符合你的需求,你将需要自己fork()然后exec(),然后杀掉重入的子进程。

对于更复杂的信号处理,你应该看标准的POSIX模块。抱歉,这几乎没有文档,但是Perl源发行包中的t/lib/posix.t文件中有一些例子。


处理后台程序的SIGHUP信号

一个通常在系统启动时启动、在系统关闭时停止的进程叫做精灵进程(Disk And Execution MONitor)。如果一个精灵进程有一个配置文件在进程启动后被改变了,应该有一种方法来告诉这个进程在不停止进程的情况下重读它的配置文件。许多精灵进程用SIGHUP信号提供了这个机制。当你想告诉精灵重读文件时,你只需发送一个SIGHUP信号给它。

并不是所有的平台都会在一个信号被执行后重新安装它们内置的信号。这意味着这个机制只能在第一次信号发送时很好地工作。这个问题的解决方法是尽量使用POSIX信号处理,它们的行为更加精确。

下面的例子实现了一个简单的精灵,每次收到一个SIGHUP信号时它将重启自身。实际的代码在子过程<c13>code()里,它只为了表示它在工作以及它将被实际地代码替换打印一点调试信息。

#!/usr/bin/perl -w use POSIX ();
use FindBin ();
use File::Basename ();
use File::Spec::Functions; $|=1; # 为了使精灵跨平台运行, exec总是使用正确的路径调用这个脚本
# 自身, 不用关心这个脚本是如何被执行起来的。 my $script = File::Basename::basename($0);
my $SELF = catfile $FindBin::Bin, $script; # POSIX unmasks the sigprocmask properly
my $sigset = POSIX::SigSet->new();
my $action = POSIX::SigAction->new('sigHUP_handler',
$sigset,
&POSIX::SA_NODEFER);
POSIX::sigaction(&POSIX::SIGHUP, $action); sub sigHUP_handler {
print "got SIGHUP\n";
exec($SELF, @ARGV) or die "Couldn't restart: $!\n";
} code(); sub code {
print "PID: $$\n";
print "ARGV: @ARGV\n";
my $c = 0;
while (++$c) {
sleep 2;
print "$c\n";
}
}
__END__

命名管道

命名管理(通常称为FIFO)是一种为了在本机进程间通信的古老的UNIX IPC机制。它工作起来就像通常连接起来的匿名管道,但是进程间通过一个文件名来共享它而不用进程相关。

为了创建命名管道,使用POSIX::mkfifo()函数。

use POSIX qw(mkfifo);
mkfifo($path, 0700) or die "mkfifo $path failed: $!";

你也可以使用Unix命令mknod(1)或者其它系统的mkfifo(1)。这些可能不在你的常规的目录下。

# 失败时返回非0,所以得用&&而不是||
#
$ENV{PATH} .= ":/etc:/usr/etc";
if ( system('mknod', $path, 'p')
&& system('mkfifo', $path) )
{
die "mk{nod,fifo} $path failed";
}

当你希望连接一个自己无关的进程时,一个fifo比较方便。当你使用fifo时,这个程序将阻塞,直到另一端有什么东西。

比如,你有你自己的.signature文件为命名管道,有一个Perl程序在另一端。现在每次有任何程序(像一个mailer、news reader、finger 程序……)尝试从这个文件读的时候,读程序将会阻塞直到你的程序提供新的signature。我们将使用管道检测参数-p来确定是否有人(或者其它)突然删除了我们的fifo。

chdir; # go home
$FIFO = '.signature'; while (1) {
unless (-p $FIFO) {
unlink $FIFO;
require POSIX;
POSIX::mkfifo($FIFO, 0700)
or die "can't mkfifo $FIFO: $!";
} # 下一行阻塞,直到有一个人来读
open (FIFO, "> $FIFO") || die "can't write $FIFO: $!";
print FIFO "John Smith (smith\@host.org)\n", `fortune -s`;
close FIFO;
sleep 2; # to avoid dup signals
}
延迟信号(安全信号)

在Perl 5.7.3以前,使用Perl代码来处理信号。由于两点原因,把你自己放在了危险之中。首先,很少的系统库函数是可重入的。如果Perl正在执行着一个函数(如malloc(3)或者printf(3))时,信号打断了,然后你的信号处理函数调用了同样的函数,你可能得到难以料到的结果-通常,是一个崩溃。其次,在较底层上,Perl自身也不是可重入的。如果Perl正在改变着它内部的数据结构,信号打断了,结果一样难以料到。

有两种态度你可以选择,即:保守或者激进。保守是说在信号处理中,尽可能少执行操作。设置一个已经有值的变量一个值,然后返回。如果你在一个比较慢的可能会重试的系统调用中,这仍然帮不了你。这意味着你不得不使用die来longjmp(3)出你的处理函数。尽管这着实有一些过于保守了,但可以防止系统除去你而避免die在一个句柄中。激进是说,“我知道风险,但是我不管”,并且在信号处理中做任意操作,然后一次一次地清除崩溃文件。

在Perl 5.7.3以及更新的版本中,避免这些问题是“延迟”-当系统发送信号给进程时(给实现Perl的C代码)一个变量被设置,然后处理马上返回。然后在一个 Perl解释器安全的时机(比如,当它要解释一个新的字节码时)这个变量被检查,然后%SIG里的Perl级别的处理被执行。这种“延迟”机制允许在信号 处理的代码中有更复杂的处理,因为我们知道Perl解释器在一个安全的状态,当信号处理被调用时,我们没有在系统库里。Perl里的实现跟以前具有如下的 不同:

长运行的字节码

当Perl解释器将要执行新的字节码时,它只查看当前的信号标志,一个长运行的字节码(比如在一个很长的字符串上执行一个正则表达式)将不会看到它直到当前的字节码执行完毕。

N.B. 如果一个信号在一个字节码执行间触发了多次,这个信号的处理只会在字节码执行完毕后被调用一

论坛徽章:
0
2 [报告]
发表于 2010-05-06 16:47 |只看该作者
次,然后所有的其它实例被丢弃。而且,如果这时候你的系统信号 队列满了,有更多的信号触发了但没有捕获(没有延迟)的时候,这些信号可以少被捕获一些其余延迟到下一个字节码,当然,有时结果会有点怪。举个例子,在调 用alarm(0)后发送的alarms信号不会停止,这时它不阻止新的alarms被触发,但是没有捕获它们。不要依赖这节中描述的行为做为当前或者以后的Perl实现的结果来考虑。
可中断IO

当一个信号被发送的时候(比如,INT control-C),操作系统阻塞在如read的IO操作上(用来实现Perl的<>操作)。在老的Perl中,这个处理被马上调用(如read不是不安全)。有了“延迟”机制,句柄不会马上被调用,如果Perl正在使用系统的stdio库,这个库会在没有给Perl一个机会来调用%SIG的处理的情况下,重试read操作。 如果这发生在你的系统上,它正用着:perlio层来处理IO-最少这些用在你希望信号调用的句柄上。:perlio层会在恢复IO操作前检查信号标志来调用%SIG句柄)。

注意默认的5.7.3及更新的Perl自动的使用:perlio层。

还要注意,一些像gethostbyname()的网络库函数有他们自己的超时机制可能会跟你自己的超时冲突。如果你使用这些函数有问题,你可以尝试POSIX的sigaction()函数,它绕过了Perl的安全信号(这并意味着你可能会有内存方面的问题)。不是设置$SIG{ALRM}:

local $SIG{ALRM} = sub { die "alarm" };

试试下面:

use POSIX qw(SIGALRM);
POSIX::sigaction(SIGALRM,
POSIX::SigAction->new(sub { die "alarm" }))
or die "Error setting SIGALRM handler: $!\n";

不使用安全信号行为的另一种方法是使用CPAN的Perl::Unsafe::Signals模块(它将影响所有的信号)。

可重启的系统调用

在支持这个的系统上,老版本的Perl在安装%SIG句柄的时候,使用SA_RESTART标志。这意味着可重启的系统调用在信号到来时会继续而不是返回。为了发送延迟的信号,Perl 5.7.3以及更新的版本不会使用SA_RESTART。因此,在以前会成功的可重启的系统调用这时会失败。$!会设为EINTR)。

注意,默认地:perlio层会如前所述重试read、write和close,而可中断的wait和waitpid调用会一直重试。

做为错误的信号

一些信号,如SEGV、ILL和BUG,通常在虚拟内存或者有其它错误的时候被创建为一种结果。存在正常错误,但是只有很少的Perl层的处理可以对付他们,所以现在Perl只马上发送它们而不是处理他们。

由操作系统触发的信号

在一些操作系统上,返回以前可以发送一些特定的信号。一个例子是CHLD或者CLD表示一个子进程执行完毕了。 在一些操作系统上,信号处理希望wait到子进程处理完毕。 在这些系统上,延迟信号机制将对这些信号(它们不对wait工作)不起作用。又一次的错误看起来像是操作系统将重试作为没有等待完毕的子进程的循环。

如果你想老的信号的行为出现在内存错误上,设置环境变量PERL_SIGNALS为"unsafe"。(Perl 5.8.1以后的新功能)。



为IPC使用open()

Perl的基本的open()语法也可以通过输出或者输入一个管道符做为第二个参数实现单向的进程间通信。这示范了如何打开一个子进程用来写入:

open(SPOOLER, "| cat -v | lpr -h 2>/dev/null")
|| die "can't fork: $!";
local $SIG{PIPE} = sub { die "spooler pipe broke" };
print SPOOLER "stuff\n";
close SPOOLER || die "bad spool: $!$?";

而这示范了如何打开一个子进程用于读出:

open(STATUS, "netstat -an 2>&1 |")
|| die "can't fork: $!";
while (<STATUS>) {
next if /^(tcp|udp)/;
print;
}
close STATUS || die "bad netstat: $!$?";

如果他能确定一个严格的程序是一个文件名在@ARGV中的Perl脚本,聪明的程序员会写成这样:

% program f1 "cmd1|" - f2 "cmd2|" f3 < tmpfile

不管从什么shell调用,这个Perl程序将从文件f1、命令cmd1,标准输入(在这里重定向为tmpfile)、文件f2、命令cmd2以及文件f3读取。非常漂亮,是吧?

你可能注意到了,你可以使用反斜线达到打开管道来读的效果。

print grep { !/^(tcp|udp)/ } `netstat -an 2>&1`;
die "bad netstat" if $?;

仅管表面上看是这样,但每次在一个时刻处理一个文件的一行或者一个记录是更有效的,因为你不用非得把整个文件读入内存。它也给了你整个的程序的最终的控制权,让你可以如你所愿地杀掉子进程。

小心地检查open()和close()的返回值。如果你正在向一个管道,你应该捕获SIGPIPE。 或者,想想看,当你打开一个指向一个不存在的程序的管道时:open()将会直接成功(它只表明fork()成功了),但是你的输出将失败-真不幸。 Perl不能知道是否一个命令工作了因为你的命令工作在一个分开的进程里,那里的exec()可能失败。所以,当从一个无效的命令读取的时候只会马上得到 一个文件结束,向一个无效的命令写入将会导致一个他们准备好处理的信号。建议:

open(FH, "|bogus") or die "can't fork: $!";
print FH "bang\n" or die "can't write: $!";
close FH or die "can't close: $!";

直到close,这不会失败,而且它会导致SIGPIPE。为了捕获它,你可以这样:

$SIG{PIPE} = 'IGNORE';
open(FH, "|bogus") or die "can't fork: $!";
print FH "bang\n" or die "can't write: $!";
close FH or die "can't close: status=$?";
文件句柄

主进程和它的所有子进程共享相同的STDIN、STDOUT和STDERR文件句柄。如果进程同时尝试访问它们,结果是未定的。你可能也想知道关闭 或者重新打开子进程的文件句柄。你可以通过open()打开你的管道得到这个,但是在一些系统上这意味着子进程不能脱离父进程而存活。


后台程序

你可以这样在后台运行一个命令:

system("cmd &");

这个命令的STDOUT和STDERR(可能有STDIN,取决于你的shell)将会与父进程一样。你不用捕获SIGCHLD因为两次fork取代了(看下面的细节)。


父进程创建的子进程的完全分离

在一些情形下(启动服务程序),你想从父进程完全脱离出子进程。这通常叫做精灵化。一个好的精灵还将chdir()到root目录(所以它不会阻止停止挂载它启动目录的分区),然后重定向标准文件描述符到/dev/null(所以随机的输出不会扰乱用户的终端)。

use POSIX 'setsid'; sub daemonize {
chdir '/' or die "Can't chdir to /: $!";
open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
open STDOUT, '>/dev/null'
or die "Can't write to /dev/null: $!";
defined(my $pid = fork) or die "Can't fork: $!";
exit if $pid;
setsid or die "Can't start a new session: $!";
open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
}

为了保证你不是一个进程组的头目,fork()不得不在setsig()之前(如果你是,setsid()会失败)。如果你的系统涮有setsid()函数,打开/dev/tty,在它上执行TIOCNOTIYioctl()代替。 看tty(4)获得详细信息。

Non-Unix用户应用查看他们的Your_OS::Process模块得到其它解决方法。


安全管道的打开

另一个有意思的IPC通信方法,是使你的单个程序进入多进程,你自己在多进程间进行通讯。open()函数将会接受一个"-|"或者"|-"做为文件的参数来做一些非常有意思的事情:它创建了一个子进程连接在了你打开的文件句柄上。这个子进程运行着如同它的父进程一样的程序。这对于正在运行在一个虚假的UDI或者GID下时,安全地打开一个文件很有用。如果你减号打开一个管道,你可以向这个打开的文件句柄写入,此时子进程会在标准输入得到它们。如果你减号打开一个管道,你可以从这个打开的文件句柄读取子进程写向标准输出的数据。

use English '-no_match_vars';
my $sleep_count = 0; do {
$pid = open(KID_TO_WRITE, "|-");
unless (defined $pid) {
warn "cannot fork: $!";
die "bailing out" if $sleep_count++ > 6;
sleep 10;
}
} until defined $pid; if ($pid) { # parent
print KID_TO_WRITE @some_data;
close(KID_TO_WRITE) || warn "kid exited $?";
} else { # child
($EUID, $EGID) = ($UID, $GID); # suid progs only
open (FILE, "> /safe/file")
|| die "can't open /safe/file: $!";
while (<STDIN>) {
print FILE; # child's STDIN is parent's KID
}
exit; #不要忘了这个
}

这个构造的另一个普遍应用是当你想执行一些东西,但却没有SHELL接口的时候。用system(),这挺直接,但是你不能安全地使用一个管道。因为没有办法来从它正在执行你的参数的时候,停止SHELL的执行。而是,使用低层次的控制来直接调用exec()。

这是一个安全保护的读取管道:

# add error processing as above
$pid = open(KID_TO_READ, "-|"); if ($pid) { # parent
while (<KID_TO_READ>) {
# do something interesting
}
close(KID_TO_READ) || warn "kid exited $?"; } else { # child
($EUID, $EGID) = ($UID, $GID); # suid only
exec($program, @options, @args)
|| die "can't exec program: $!";
# NOTREACHED
}

这是一个安全的打开用来写入的管道:

# add error processing as above
$pid = open(KID_TO_WRITE, "|-");
$SIG{PIPE} = sub { die "whoops, $program pipe broke" }; if ($pid) { # parent
for (@data) {
print KID_TO_WRITE;
}
close(KID_TO_WRITE) || warn "kid exited $?"; } else { # child
($EUID, $EGID) = ($UID, $GID);
exec($program, @options, @args)
|| die "can't exec program: $!";
# NOTREACHED
}

从Perl 5.8.0开始,你也可以使用列表等式的open语法来操作管道:语法

open KID_PS, "-|", "ps", "aux" or die $!;

创建了一个ps(1)命令(没有交互shell,但是有多于三个的选项传给了open()),然后从文件句柄KID_PS读取它的标准输出。对应的用来写入的管道的语法(用"|-"来代替"-|")也实现了。

补充一下,这些操作都是Unix族的分离,即它们有可能在其它系统实现上失败。另外,没有真正地多线程。如果你想多了解一些关于线程的东西,看下面SEE ALSO部分提到的modules文件。


与其它进程双向通信

现在,这对于单向的通讯不错,可是如何实现双向的通讯呢?明显的,你想这样做,但是不成:

open(PROG_FOR_READING_AND_WRITING, "| some program |")

如果你忘记了使用use warnings或者-w选项,你将会得到这样的错误结果:

Can't do bidirectional pipe at -e line 1.

如果你真的想做

论坛徽章:
0
3 [报告]
发表于 2010-05-06 16:47 |只看该作者

,你可以使用标准的open2()库函数来连接上两端。并且,为了完全地I/O控制所以你想截获标准错误,也有一个open3()。但是,那样做需要一个select()循环来允许你使用标准的Perl输入操作。

如果你查看它的源代码,你将会看到open2()使用了低层次的像Unix的pipe()和exec()的调用来创建了所有的连接。而如果使用socketpair()则会有些微地性能提升,它也会更小巧一点。open2()和open3()函数并不能在除了Unix或者其它的遵循POSIX的系统上工作的很好。

这是使用open2()的例子:

use FileHandle;
use IPC::Open2;
$pid = open2(*Reader, *Writer, "cat -u -n" );
print Writer "stuff\n";
$got = <Reader>;

这程序的问题是,Unix的缓冲区会使得问题比较复杂。尽管你的Writer</C0>文件句柄是自动缓冲的,另一端的进程也可能及时地收到你的数据,你不能强制地要求它在你的请求中快速返回。在这种情况下,我们可以,给cat一个-u选项来使它不缓冲。但是只有很少地Unix命令是定义为通过管道的,所以这个方法只能工作在你自己写的能通过管道的程序上。

一个解决方法是使用不标准的Comm.pl库。它使用虚拟终端来使你程序的行为更合理。

require 'Comm.pl';
$ph = open_proc('cat -n');
for (1..10) {
print $ph "a line\n";
print "got back ", scalar <$ph>;
}

使用这个方法,你不用再不得不自己控制你使用的程序的源代码。Comm库也支持expect()和interact()函数。你可以在下面的SEE ALSO块里提到的最近的CPAN归档上找到这个库(我们希望它的IPC::Chat)。

CPAN上的更新的Expect.pm模块也能干这个事儿。这个模块需要CPAN上的IO::Pty和IO::Stty两个模块。它设置一个虚拟终端来和你的程序交互,使得如同与一个真实的设备驱动的终端交谈一样。如果你的系统都支持,这可能是你的万幸。


与自身双向通信

如果你想,你可以使用低层次的pipe()和fork()来手动组合。这个例子只是和自己交互,但是你可以重新打开文件句柄来操作标准输入、标准输出或者调用其它进程。

#!/usr/bin/perl -w
# pipe1 - bidirectional communication using two pipe pairs
# designed for the socketpair-challenged
use IO::Handle; # thousands of lines just for autoflush :-(
pipe(PARENT_RDR, CHILD_WTR); # XXX: failure? pipe(CHILD_RDR, PARENT_WTR); # XXX: failure? CHILD_WTR->autoflush(1);
PARENT_WTR->autoflush(1); if ($pid = fork) {
close PARENT_RDR; close PARENT_WTR;
print CHILD_WTR "Parent Pid $$ is sending this\n";
chomp($line = <CHILD_RDR>);
print "Parent Pid $$ just read this: `$line'\n";
close CHILD_RDR; close CHILD_WTR;
waitpid($pid,0);
} else {
die "cannot fork: $!" unless defined $pid;
close CHILD_RDR; close CHILD_WTR;
chomp($line = <PARENT_RDR>);
print "Child Pid $$ just read this: `$line'\n";
print PARENT_WTR "Child Pid $$ is sending this\n";
close PARENT_RDR; close PARENT_WTR;
exit(1);
}

但是你实在不用做两次pipe调用。如果你使用socketpair()系统调用,它将会为你做好这些。

#!/usr/bin/perl -w
# pipe2 - bidirectional communication using socketpair
# "the best ones always go both ways" use Socket;
use IO::Handle; # thousands of lines just for autoflush :-(
# We say AF_UNIX because although *_LOCAL is the
# POSIX 1003.1g form of the constant, many machines
# still don't have it.
socketpair(CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
or die "socketpair: $!"; CHILD->autoflush(1);
PARENT->autoflush(1); if ($pid = fork) {
close PARENT;
print CHILD "Parent Pid $$ is sending this\n";
chomp($line = <CHILD>);
print "Parent Pid $$ just read this: `$line'\n";
close CHILD;
waitpid($pid,0);
} else {
die "cannot fork: $!" unless defined $pid;
close CHILD;
chomp($line = <PARENT>);
print "Child Pid $$ just read this: `$line'\n";
print PARENT "Child Pid $$ is sending this\n";
close PARENT;
exit(1);
}


原文地址 http://doc.perl.org

论坛徽章:
0
4 [报告]
发表于 2010-05-06 19:28 |只看该作者
排版有问题。建议整理好格式。

论坛徽章:
0
5 [报告]
发表于 2010-05-07 16:10 |只看该作者
CHROME brower 就是比较讨厌     排版总是很差

论坛徽章:
0
6 [报告]
发表于 2010-05-07 17:45 |只看该作者
好东西大家共享,good,我飘飘荡荡,怀疑进入了共产主义社会

论坛徽章:
0
7 [报告]
发表于 2010-05-07 23:14 |只看该作者
支持下~
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP