📄 ch13.htm
字号:
available. Generally, shared memory and semaphores have to be
used together. When working with large blocks of data, use shared
memory to pass the data between two processes. Synchronize the
transfer between processes via the use of a semaphore.
<P>
What if you have a situation in which more than one process is
required to process the data? Semaphores can get clunky at this
stage if you are not careful.
<P>
In a typical scenario, you could have one process collect data
from external devices and then have the data available in shared
memory for all other processes. The shared memory area will be
divided into partitions. Each partition is used only by one process
and only written to by the data collector. The data collector
updates all the sections of shared memory and then updates a semaphore
with the number of processes that are currently waiting to work
with this data. Then it sends a message to each of the processes
via a message queue. After sending all the messages, the data
collector process waits for the semaphore to be <TT><FONT FACE="Courier">0</FONT></TT>
again, thereby getting the signal to proceed.
<P>
Each data-handling process (client) can wait for messages forever
on its message queue. As soon as it receives a message on its
queue, the client can guarantee that it will have exclusive access
to its partition. After it has processed the data, the client
can decrement the semaphore. As each client increments the semaphore,
it will go back to the top of the loop and wait on the input message
queue again.
<P>
Once all the clients have incremented the semaphore, it becomes
<TT><FONT FACE="Courier">0</FONT></TT> again. This causes the
data collector to wake up and begin the process of collecting
and updating the shared memory area.
<P>
Listings 13.7 and 13.8 show a partial application for such a system.
These listings are by no means complete because this would require
a full-blown application well beyond the scope of this chapter.
The gist of the program is to illustrate how all three types of
Ipc objects can be used with each other to create relatively complex
applications.
<P>
The server application decrements the semaphore to block (while
the clients do what they have to do) and then increments the value
of the semaphore. The processes here have to run concurrently
in the background. There are three clients for the one server.
Obviously, this example is contrived for the book-you might have
more clients to handle your tasks.
<HR>
<BLOCKQUOTE>
<B>Listing 13.7. The server of a dummy application.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 #!/usr/bin/perl<BR>
2 unshift (@Inc,"/usr/lib/perl5/i486-linux/5.002/sys");
<BR>
3 unshift (@Inc,"/usr/lib/perl5/i486-linux/5.002/linux");
<BR>
4 unshift (@Inc,"/usr/lib/perl5/i486-linux/5.002/asm");
<BR>
5 require "ipc.ph";
<BR>
6 require "shm.ph";<BR>
7 $PERMISSIONS=0666;
<BR>
8 $ipckey = 42;<BR>
9 $size = 4096;<BR>
10 #<BR>
11 # Create the shared memory segment<BR>
12 #<BR>
13 $| = 1;<BR>
14 @messages = (<BR>
15 "Process 0 ",<BR>
16 'Process 1 ',<BR>
17 'Process 2 ',<BR>
18 );<BR>
19 $shmid = shmget($ipckey, $size, &Ipc_CREAT | $PERMISSIONS);
<BR>
20 $count = $#messages + 1;<BR>
21 $semid = semget($ipckey,$count,&Ipc_CREAT|$PERMISSIONS);
<BR>
22 $semflags = 0;<BR>
23 for ($offset = 0; $offset < $count; $offset++) {<BR>
24 $msg = $messages[$offset];<BR>
25 $msgid[$offset] = msgget($ipckey <BR>
</FONT></TT> <TT><FONT FACE="Courier">$offset,&Ipc_CREAT
| $PERMISSIONS);<BR>
26 print "\n Server: Creating Message
Queue: "<BR>
</FONT></TT> <TT><FONT FACE="Courier">.
$msgid[$offset] . "\n";<BR>
27 }<BR>
28 printf "\n Shared Memory Created Id = $shmid";<BR>
29 while(1)<BR>
30 {<BR>
31 $semop = $count; #
Stop the clients<BR>
32 $semopstr = pack("sss",$semnum,$semop,$semflags);
<BR>
33 die "Cannot get semaphore"
unless semop($semid,$semopstr);<BR>
34<BR>
35 for ($offset = 0; $offset
< $count; $offset++) {<BR>
36
$semnum = $offset;<BR>
37
$msg = $messages[$offset];<BR>
38
print "\n Server: Writing " . $msg . <BR>
</FONT></TT> <TT><FONT FACE="Courier">"
at " . $offset * 40 . "\n";<BR>
39
shmwrite($shmid, $msg, $offset * 40, 12);<BR>
40
$msg = pack("L a*", $offset + 1, " Go for it!");
<BR>
41
print "\n Server: Sending to" . $msgid[$offset] . "\n";
<BR>
42
msgsnd($msgid[$offset], "$msg", &Ipc_NOWAIT);<BR>
43 }<BR>
44 $semop = -$count;
# Block till semaphore is 0<BR>
45 $semopstr = pack("sss",$semnum,$semop,$semflags);
<BR>
46 die "Cannot get semaphore
to wait" unless semop($semid,$semopstr);<BR>
47 sleep(10);<BR>
48 }</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
Listing 13.8 is the client application to pick up the messages
from the server. The messages sent can contain additional information
for the client; that is, they don't have to be just triggers for
the client to proceed with reading. The contents of the messages
can contain information about how and where to pick up data from
shared memory.
<HR>
<BLOCKQUOTE>
<B>Listing 13.8. The client of a dummy application.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 #!/usr/bin/perl<BR>
2 #<BR>
3 # Usage = client
-p ProcessIndex<BR>
4 #<BR>
5 unshift (@Inc,"/usr/lib/perl5/i486-linux/5.002/sys");
<BR>
6 unshift (@Inc,"/usr/lib/perl5/i486-linux/5.002/linux");
<BR>
7 unshift (@Inc,"/usr/lib/perl5/i486-linux/5.002/asm");
<BR>
8 use Getopt::Long;<BR>
9 $result = GetOptions
('p=i');<BR>
10 $| = 1;<BR>
11 if (($opt_p < 0) || ($opt_p > 3)) {<BR>
12 die "Usage: $0 -p ProcessIndex";
<BR>
13 exit(0);<BR>
14 }<BR>
15 require "ipc.ph";<BR>
16 require "shm.ph";<BR>
17 require "msg.ph";<BR>
18 require "sem.ph";<BR>
19 $PERMISSIONS=0666;<BR>
20 $msg_type = 0;<BR>
21 $msg_offset = $opt_p * 40;<BR>
22 $ipckey = 42;<BR>
23 $msg_key = $ipckey + $opt_p;<BR>
24 $size = 0;<BR>
25 $shmid = shmget($ipckey, $size, &Ipc_CREAT | $PERMISSIONS);
<BR>
26 printf "\n $$ = Shared Memory Id = $shmid";<BR>
27 $msgid = msgget($msg_key,&Ipc_CREAT | $PERMISSIONS);<BR>
28 printf "\n $$ = Message Id <BR>
</FONT></TT> <TT><FONT FACE="Courier">=
$msgid, Will read from $msg_offset, $msg_type\n";<BR>
29 $semid = semget($ipckey,3,&Ipc_CREAT|$PERMISSIONS);<BR>
30 $semnum = 0;<BR>
31 $semflags = 0;<BR>
32 while (1)<BR>
33 {<BR>
34 print "\n Read message
of type :". $msg_type;<BR>
35 msgrcv($msgid, $msg, 80,
$msg_type, 0);<BR>
36 $retval = shmread($msgid,
$message, 0, 80);<BR>
37 print "\n Read message:".
$message. "ret value= $retval" ;<BR>
38 $semop = 1; #
Clear yourself for server<BR>
39 $semopstr = pack("sss",$semnum,$semop,$semflags);
<BR>
40 die "Cannot get semaphore"
unless semop($semid,$semopstr);<BR>
41 print "\n After semaphore";
<BR>
42 }</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
The sample programs shown in Listings 13.7 and 13.8 provided the
basis for a prototype of a seismic data collection system. The
actual system was written in C for efficiency because of some
pretty lengthy mathematical calculations. However, with Perl,
we were able to use this code to get a proof-of-concept working
model up and running in just one afternoon. The prototype provided
us with enough information to consider using the Ipc model for
the application. In later models of the same application, I was
able to extend the processing to remote machines by replacing
the message queues with sockets and sending the requisite portions
of data along the sockets.
<P>
The final application was tested further by adding new Perl scripts
that share messages and simulate data using shared memory. Listings
13.7 and 13.8 are very similar to the working application and
have been created from scratch. It should be relatively painless
for you to take this code and extend it into your own prototype.
<H2><A NAME="Summary"><FONT SIZE=5 COLOR=#FF0000>Summary</FONT></A>
</H2>
<P>
Perl is a very powerful tool for prototyping applications. With
the capability to access the system facilities, Perl can provide
the necessary tools for rapid prototyping. Hopefully, this chapter
has provided you with enough information to set up your own applications.
<P>
This chapter has introduced you to the System V Ipc facilities
available through Perl. The Ipc objects are global and remain
in memory long after the processes that created them are gone.
With Ipc objects, you are limited to one machine. If your application
requires network access, consider using sockets instead.
<P>
Using message queues, you can send messages between processes.
For large data items, message queues might not be very efficient.
Consider using shared memory instead. To synchronize the access
to the shared memory, you can use semaphores to prevent one process
from writing to an area where another process might be reading.
<P>
<HR WIDTH="100%"></P>
<CENTER><P><A HREF="ch12.htm" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/ch12.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="ch14.htm" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/ch14.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 + -