📄 ch14.htm
字号:
fixed length records you would use:<BR>
32 # read
PIPE_R,$buf,20;<BR>
33 print
"Child: Received $buf \n";<BR>
34 }<BR>
35 }</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
Line 2 of this listing sets the output buffers to be flushed as
soon as they are written to. Line 6 creates the pipe for you to
make <TT><FONT FACE="Courier">PIPE_R</FONT></TT> and <TT><FONT FACE="Courier">PIPE_W</FONT></TT>
valid handles. We immediately do a fork in line 10.
<P>
After the fork, there are actually four handles: two <TT><FONT FACE="Courier">PIPE_R</FONT></TT>
handles and two <TT><FONT FACE="Courier">PIPE_W</FONT></TT> handles.
Because the parent does not want to read its own echo on <TT><FONT FACE="Courier">PIPE_R</FONT></TT>,
it closes <TT><FONT FACE="Courier">PIPE_R</FONT></TT>. Similarly,
the child has no reason to write to itself, so it closes <TT><FONT FACE="Courier">PIPE_W</FONT></TT>.
Then the parent writes to the <TT><FONT FACE="Courier">PIPE_W</FONT></TT>
handle while the child reads from the <TT><FONT FACE="Courier">PIPE_R</FONT></TT>
handle.
<P>
The following lines are the output from this program. Note how
the output from the child and parent process is intermixed. This
is because both processes are running at the same priority and
accessing the output device (<TT><FONT FACE="Courier">stdout</FONT></TT>)
at the same time.
<BLOCKQUOTE>
<TT><FONT FACE="Courier">hmm.. okParent:<BR>
Child:Child: Received [Sending: 0]<BR>
<BR>
Child: Received [Sending: 1]<BR>
<BR>
Child: Received [Sending: 2]<BR>
<BR>
Child: Received [Sending: 3]<BR>
<BR>
Child: Received [Sending: 4]</FONT></TT>
</BLOCKQUOTE>
<P>
Pipes are great when used between related processes. However,
if you want to communicate between two different processes, you
have to use FIFOs. A <I>FIFO</I> is also referred to as a <I>named
pipe</I>. Unrelated processes use named pipes to talk to each
other. The FIFO appears like a normal filename.
<P>
In Listing 14.4, line 6 names the FIFO pathname as it would appear
in the output from an <TT><FONT FACE="Courier">ls</FONT></TT>
command. In line 7, the FIFO is created with a system call to
the command <TT><FONT FACE="Courier">mkfifo</FONT></TT> using
the <TT><FONT FACE="Courier">-m</FONT></TT> option and pathname
used in line 6. The <TT><FONT FACE="Courier">unless</FONT></TT>
clause checks to see whether such a FIFO already exists before
attempting to recreate it. The code in line 8 actually opens the
FIFO after it is created. Then, in lines 9 through 11, you create
a string with the current date and send it to the FIFO. In line
12, you close the FIFO. The FIFO is not destroyed when it is closed
at line 12. You have to unlink the pathname-that is, remove the
FIFO by name to get rid of it, which is shown in line 14.
<P>
You can use an <TT><FONT FACE="Courier">ls</FONT></TT> command
on the FIFO name to see whether it exists. A way to test whether
a filename is a FIFO is to use the option <TT><FONT FACE="Courier">-p</FONT></TT>
on the filename from within a Perl script.
<HR>
<BLOCKQUOTE>
<B>Listing 14.4. Using FIFOs.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 #!/usr/bin/perl<BR>
2 #<BR>
3 # Create a fifo and
then return the date<BR>
4 # back to the caller when FIFO is read.<BR>
5 #<BR>
6 $path = "./ch14_fifo";<BR>
7
unless (-p $path) { system("mkfifo -m 0666 $path");
}<BR>
8 open(FIFO,"> $path")
|| die "Cannot open $! \n";<BR>
9
$date = `date`;<BR>
10 chop($date);<BR>
11 print FIFO "[$date]";<BR>
12 close FIFO;<BR>
13 # Remove when done.<BR>
14 unlink $path;</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
The FIFO is opened in line 8. Now the program blocks until there's
something on the other end trying to read from it. Using a command
like <TT><FONT FACE="Courier">cat ch14_fifo</FONT></TT> triggers
line 9. The program then gets the system date and prints it out
to the FIFO. Line 14 cleans up after itself.
<P>
You would probably want to have a signal handler to clean up if
a terminating signal arrives before input is read from the FIFO
and the program exits before destroying the FIFO. This is the
reason for the <TT><FONT FACE="Courier">unless</FONT></TT> clause,
which checks for any existing FIFOs before creating a new one.
The signal to catch is <TT><FONT FACE="Courier">SIGPIPE</FONT></TT>
for broken pipes. To trap <TT><FONT FACE="Courier">SIGPIPE</FONT></TT>,
you have to add this segment to your code:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">sub pipeHandler {<BR>
my $sig = shift @_;<BR>
</FONT></TT> <TT><FONT FACE="Courier">print
" Caught SIGPIPE: $sig $1 \n";<BR>
exit(1);<BR>
}<BR>
<BR>
$SIG{PIPE} = \&pipeHandler;</FONT></TT>
</BLOCKQUOTE>
<H2><A NAME="UsingopenforIpc"><FONT SIZE=5 COLOR=#FF0000>Using
</FONT><TT><FONT SIZE=5 COLOR=#FF0000 FACE="Courier">open()</FONT></TT><FONT SIZE=5 COLOR=#FF0000>
for Ipc</FONT></A></H2>
<P>
The <TT><FONT FACE="Courier">open()</FONT></TT> statement can
be used to start communication pipes. The catch is that these
pipes are unidirectional. To read the results from a process,
put <TT><FONT FACE="Courier">|</FONT></TT> at the end of the command
being executed. To write your results to the standard input of
a process, put <TT><FONT FACE="Courier">|</FONT></TT> at the start
of the command.
<P>
For example, here's how to write your results to the sendmail
mailing program:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">open(LOG, "| /usr/apps/formatData
| /usr/bin/sendmail ");</FONT></TT>
</BLOCKQUOTE>
<P>
To read the results back from a process, use a line like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">open(READINGS, "/usr/apps/commProgram
|");</FONT></TT>
</BLOCKQUOTE>
<P>
As an example, consider the program in Listing 14.5, which prints
the names of the files with the string passed to them at the command-line
argument. It then lists all the files within the associative array
<TT><FONT FACE="Courier">fname</FONT></TT>.
<HR>
<BLOCKQUOTE>
<B>Listing 14.5. Storing results of a command in an array.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 #!/usr/bin/perl<BR>
2 # Storing results from
a program in an array.<BR>
3 my %fname = ();<BR>
4 # Open all files with names ending in .pl<BR>
5 open(IncOMING,"grep
ARGV[1] *.pl |");<BR>
6 while (<IncOMING>) {<BR>
7
($name,@line) = split(':',$_);<BR>
8 if (!$fname{$name}) {<BR>
9 $fname{$name}
= $name;<BR>
10 }<BR>
11 }<BR>
12 close IncOMING;<BR>
13<BR>
14 while (($key,$value) = each (%fname)) {<BR>
15 print "File: " . $key . "\n";
<BR>
16 }</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
It's probably tempting to use the back ticks to run a program
for you and collect the results. For example, <TT><FONT FACE="Courier">$a
= `who`</FONT></TT> returns the entire results of the <TT><FONT FACE="Courier">who</FONT></TT>
command in variable <TT><FONT FACE="Courier">$a</FONT></TT>. It's
impractical to use this method to collect results from a verbose
command. Using a command like <TT><FONT FACE="Courier">$a=`ls
-lr`</FONT></TT> is a lot slower than actually opening a file
handle to the output of this command and then processing it one
line at a time. The problem is that the entire result of the command
is stored in variable <TT><FONT FACE="Courier">$a</FONT></TT>,
chewing up memory and time while <TT><FONT FACE="Courier">$a</FONT></TT>
is appended to. It's easier and far more efficient to simply read
from the output in manageable chunks.
<P>
Use two pipes to do bidirectional communications. Do not use a
statement with a <TT><FONT FACE="Courier">|</FONT></TT> at end
of the command. Perl does not allow commands using this syntax:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">open(HANDLE, "| sort |");</FONT></TT>
</BLOCKQUOTE>
<P>
Perl gives an error message about not being able to perform bidirectional
pipes.
<P>
The <TT><FONT FACE="Courier">open()</FONT></TT> function can accept
a file argument of either <TT><FONT FACE="Courier">-|</FONT></TT>
or <TT><FONT FACE="Courier">|-</FONT></TT>. Accepting either of
these parameters forks a child connected to the file handle you've
just opened. The child is then running the same program as the
parent. The return value from the call to <TT><FONT FACE="Courier">open()</FONT></TT>
is the process ID of the child or zero for the parent. The function
dies if it cannot fork within the <TT><FONT FACE="Courier">open()</FONT></TT>
call. Here's a sample usage of <TT><FONT FACE="Courier">-|</FONT></TT>
to create a receiving child:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$| = 1; # Always do
unbuffered IO here.<BR>
<BR>
$pid = open(MYHANDLE, "|-");<BR>
if ($pid == 0) {<BR>
</FONT></TT> <TT><FONT FACE="Courier">#parent
acts as server<BR>
print MYHANDLE, " something";
<BR>
} else {<BR>
#child acts as receiver.<BR>
</FONT></TT> <TT><FONT FACE="Courier">getSomething
= <MYHANDLE>;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
An implicit <TT><FONT FACE="Courier">fork()</FONT></TT> is possible
with the <TT><FONT FACE="Courier">open2()</FONT></TT> command.
Basically, with this call you start off two processes running
the same code. The child process ID is returned to the client.
Errors cause the function to bail out. The <TT><FONT FACE="Courier">Ipc::Open2</FONT></TT>
module is required for this to work. Also, you have to make sure
you are reading and writing continuously to this buffer. Be careful
to run only programs for which you know the input and output sequences.
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$| = 1;<BR>
$pid = open('BI_R', 'BI_W', "myPerlScript");<BR>
if ($pid == 0) {<BR>
#parent acts as server<BR>
</FONT></TT> <TT><FONT FACE="Courier">print
B_R, " something";<BR>
} else {<BR>
</FONT></TT> <TT><FONT FACE="Courier">#child
acts as receiver.<BR>
getSomething = <MYHANDLE>;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
There is an <TT><FONT FACE="Courier">open3()</FONT></TT> function
call, too, that adds <TT><FONT FACE="Courier">STDERR</FONT></TT>
to the list of opened files with which you may work.
<H2><A NAME="Summary"><FONT SIZE=5 COLOR=#FF0000>Summary</FONT></A>
</H2>
<P>
This is a very quick introduction to using UNIX pipes and signals
with Perl. Signals are a way of asynchronously telling a process
about an event. Generally, signals can only be caught and an error
message displayed about the type of signal through the use of
a subroutine, called a <I>handler</I>. The types of signals vary
depending on the type of UNIX system you use.
<P>
Using pipes is a method of communication between two processes
that is old, yet still heavily used in UNIX. If a pipe (<TT><FONT FACE="Courier">|</FONT></TT>)
is placed at the beginning of a filename in an <TT><FONT FACE="Courier">open()</FONT></TT>
call, you'll be writing to a pipe with a UNIX command, file, or
device at the other end. If <TT><FONT FACE="Courier">command |</FONT></TT>
is placed at the end of a filename to the <TT><FONT FACE="Courier">open()</FONT></TT>
call, you'll be reading the output of the command. Bi-directional
and even tridirectional pipes are possible using the <TT><FONT FACE="Courier">open2()</FONT></TT>
and <TT><FONT FACE="Courier">open3()</FONT></TT> calls.
<P>
<HR WIDTH="100%"></P>
<CENTER><P><A HREF="ch13.htm" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/ch13.htm"><IMG SRC="pc.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/pc.gif" BORDER=0 HEIGHT=88 WIDTH=140></A><A HREF="#CONTENTS"><IMG SRC="cc.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/cc.gif" BORDER=0 HEIGHT=88 WIDTH=140></A><A HREF="index.htm" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/index.htm"><IMG SRC="hb.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/hb.gif" BORDER=0 HEIGHT=88 WIDTH=140></A><A HREF="ch15.htm" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/ch15.htm"><IMG
SRC="nc.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/nc.gif" BORDER=0 HEIGHT=88 WIDTH=140></A></P></CENTER>
<P>
<HR WIDTH="100%"></P>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -