old-www/LDP/LG/issue89/raghu.html

445 lines
20 KiB
HTML

<!--startcut ==============================================-->
<!-- *** BEGIN HTML header *** -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML><HEAD>
<title>Exploring Message Queues, part 1 LG #89</title>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
ALINK="#FF0000">
<!-- *** END HTML header *** -->
<!-- *** BEGIN navbar *** -->
<!-- *** END navbar *** -->
<!--endcut ============================================================-->
<TABLE BORDER><TR><TD WIDTH="200">
<A HREF="http://www.linuxgazette.com/">
<IMG ALT="LINUX GAZETTE" SRC="../gx/2002/lglogo_200x41.png"
WIDTH="200" HEIGHT="41" border="0"></A>
<BR CLEAR="all">
<SMALL>...<I>making Linux just a little more fun!</I></SMALL>
</TD><TD WIDTH="380">
<CENTER>
<BIG><BIG><STRONG><FONT COLOR="maroon">Exploring Message Queues, part 1</FONT></STRONG></BIG></BIG>
<BR>
<STRONG>By <A HREF="../authors/raghu.html">Raghu J Menon</A></STRONG>
</CENTER>
</TD></TR>
</TABLE>
<P>
<!-- END header -->
<p>Message queues are one of the three IPC (Inter Process Communication)
facilities provided by the UNIX operating system apart from semaphores and
shared memory. Message queues appeared in an early release of UNIX system V
release III as a means of passing messages between processes in an asynchronous
way.</p>
<p>Let us look at what a message queue actually means. In order for two or more
processes to communicate with each other, one of them places a message in the
message queue through some message passing module of the operating system. This
message placed can be read by some other process. However the access to the
message by the process is preceded by a clause&nbsp; that the queue and the
process share a common key.</p>
<H2>A short note on the ipcs command</H2>
<p>The ipcs command displays the currently resident ipcs in the
operating system. Try typing this command at the prompt, you will obtain an
output similar to this</p>
<p>&nbsp; ------ Shared Memory Segments --------<br>
key shmid owner perms bytes nattch status <br>
<br>
------ Semaphore Arrays --------<br>
key semid owner perms nsems status <br>
<br>
------ Message Queues --------<br>
key msqid owner perms used-bytes messages <br>
<br>
Our interest is in the last one. A short&nbsp; description of each of the fields
will come in handy as we proceed further,</p>
<ul>
<li>key-It is the name that we give to the queue when we create it
using the msgget() function. </li>
<li>msqid-It is the value returned by the msgget() function call.
The operating system recognizes the queue by this name. Any manipulations on the
queue are based on its id. </li>
<li>owner-It specifies the creator of the queue.</li>
<li>perms-The octal value indicated in this field are the
permissions for various users on the queue. You could compare it with permissions
granted on a file.</li>
<li>used-bytes-It indicates the number of bytes of the queue
currently in use.</li>
<li>messages-This field indicates the number of messages in the
queue.</li>
</ul>
<p>&nbsp; </p>
<H2>Creating a Message Queue</H2>
<p>All the IPCs are created using some ipcget() function. Message queues are
created using the msgget() function. It takes&nbsp; 2 parameters, the key which
signifies the name given to the queue and a flag variable. The flag variable can
be either IPC_CREAT or IPC_EXCL. The first of these creates a queue if one does
not already exist else it simply ignores the parameter. The second forces an
error message to be flashed onto the screen declaring that a queue by that name
exists and cloning is unethical in this part of the world. What does the
function return? Well i suppose you guessed it, it returns the message queue id
(similar to a file descriptor).
Now go through the code below and try it on your computer: <a href="misc/raghu/mesg1.c.txt">mesg1.c.</a> The code
creates a queue by name 10(This is passed as the first parameter). The key_t data type is nothing but int, do not be confused by it. Now how do you
ascertain that a queue has indeed been created. Try the following command at
your prompt i.e.&nbsp; ipcs . This command provides you with data pertaining to
the IPCs that are living in your system at present. Scan the message queue
section, you see an entry which has a value of 10(in hex) under the key field
and a value of 0(usually) under the id field. This entry corresponds to the
queue that we created above. If you have any doubts regarding that try the
command before running the program, and see for yourself the difference in the
output generated.</p>
<p>------ Shared Memory Segments --------<br>
key shmid owner perms bytes nattch status <br>
<br>
------ Semaphore Arrays --------<br>
key semid owner perms nsems status <br>
<br>
------ Message Queues --------<br>
key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
msqid&nbsp;&nbsp;&nbsp;&nbsp; owner&nbsp;&nbsp;&nbsp; perms&nbsp;&nbsp;&nbsp;&nbsp;
used-bytes messages <br>
0x0000000a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 666&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
0 <br>
<br>
Okay here is an exercise for you, try replacing the flag
variable with&nbsp; IPC_CREAT | IPC_EXCL, recompile and run the code. The result
is obvious since a queue by that name already exists we will encounter an error
message. Another point to be considered is the value returned by the msgget()
function incase a queue cannot be created, as is the case when a queue by the id
already exists and we use the IPC_EXCL flag in the call to msgget(). The return&nbsp;
value in such a case is a negative value. If we want the queue with the id to be
created we need to remove the one already present with the same id, well just
type the following at the command prompt ipcrm msg &lt;id-number&gt;.
</p>
<H2>Queue Permissions</H2>
<p>A queue as created above is more of a church bell that can be rung by anyone.
What i actually&nbsp; mean is that just as files have permission fields that
restrict their access and modification by users of the operating system, so do
queues. To set the permission fields&nbsp; for a message queue uses the flag
parameter in the msgget() function along with IPC_CREAT and IPC_EXCL. To specify
the permissions we need to OR IPC_CREAT with the value in octal that signifies
the permission. Try out the code below: <a href="misc/raghu/mesg.c.txt">mesg.c</a> There is no difference from the previous
one except that the second field in the msgget() function has the value 0644
ORed with it. This queue is created with read-write mode for the owner and read
only mode for everyone else. Thus we have a queue that is available for every
user of the system but only granting read permission. A point to be noted in this context is
that it is meaningless to have execute permission granted as a queue cannot be
executed only a code can be executed). A read-write permission to all would mean
to use the value 0666 instead of the one specified above.&nbsp; </p>
<H2>Where is the queue information stored?</H2>
<p>For every queue that we create, the information is stored in
the following structure.</p>
<p>The structure has been defined in the file bits/msg.h. For the
purpose of file inclusion in our programs though we use the file sys/msg.h</p>
<PRE>
/* Structure of record for one message inside the kernel.
The type `struct msg' is opaque. __time_t is of type long int. All the data
types are defined in types.h header file*/
struct msqid_ds
{
struct ipc_perm msg_perm; /* structure describing operation permission */
__time_t msg_stime; /* time of last msgsnd (see below ) command */
unsigned long int __unused1;
__time_t msg_rtime; /* time of last msgrcv (see below) command */
unsigned long int __unused2;
__time_t msg_ctime; /* time of last change */
unsigned long int __unused3;
unsigned long int __msg_cbytes; /* current number of bytes on queue */
msgqnum_t msg_qnum; /* number of messages currently on queue */
msglen_t msg_qbytes; /* max number of bytes allowed on queue */
__pid_t msg_lspid; /* pid of last msgsnd() */
__pid_t msg_lrpid; /* pid of last msgrcv() */
unsigned long int __unused4;
unsigned long int __unused5;
};
</PRE>
<p>The first element of the structure is another structure which
has the following declaration in bits/ipc.h,for inclusion purpose we use the
file sys/ipc.h.
<PRE>
/* Data structure used to pass permission information to IPC operations. */
struct ipc_perm
{
__key_t __key; /* Key. */
__uid_t uid; /* Owner's user ID. */
__gid_t gid; /* Owner's group ID. */
__uid_t cuid; /* Creator's user ID. */
__gid_t cgid; /* Creator's group ID. */
unsigned short int mode; /* Read/write permission. */
unsigned short int __pad1;
unsigned short int __seq; /* Sequence number. */
unsigned short int __pad2;
unsigned long int __unused1;
unsigned long int __unused2;
};
</PRE>
&nbsp;</p>
<p>The ipc_perm is the structure dealing with user,group id's and
permissions. </p>
<H3>Controlling The Message Queue</H3>
<p>A queue once created can be modified. This implies that the
creator or an authorized user can change the permissions and characteristics of
the queue. The function msgctl() is used for carrying out the modifications. The
function has the following definition.</p>
<p>int msgctl(int msqid, int cmd, struct msqid_ds *queuestat )<br>
<br>
The first parameter msqid&nbsp; is the id of the queue that we intend to modify,
the value must be one that already exists.</p>
<p>The cmd argument can be any one of the following.</p>
<ul>
<li>IPC_STAT -- Place information about the status of the queue in
the data structure pointed to by queuestat. The process must have read
permission for this call to succeed. This option essentially fills the structure
that we defined above with the information of the queue with id msqid.&nbsp;&nbsp;
<br>
&nbsp;</li>
<li>IPC_SET -- Set the owner's user and group ID, the permissions,
and the size (in number of bytes) of the message queue. A process must have the
effective user ID of the owner, creator, or superuser for this call to succeed.
Under this option the user is granted the freedom to modify the fields within
the structure msqid_ds of the queue with id msqid. But the freedom is limited
though as the only fields modifiable are msqid_ds.msg_perm.uid,
msqid_ds.msg_perm.gid, msqid_ds.msg_perm.mode and msg_qbytes. The msqid_ds in
our case&nbsp; the pointer queuestat , msg_perm has been
defined as of type struct ipc_perm in the msqid_ds struct which has been
explained above.</li>
<li>&nbsp;IPC_RMID -- Remove the message queue specified by the msqid argument. That needs
no more explanation.</li>
</ul>
<p><br>
The following c code will illustrate elements within the structure:
<a href="misc/raghu/qinfo.c.txt">qinfo.c</a> The message queue id number is passed
as a command line argument. This id is that of an already present queue, so
select one from the output of the ipcs command. The msgctl() function fills in
the structure pointed to by qstatus which is of type struct msqid_ds. The rest
of the code just prints various characteristics of the queue. It will be a good
idea to just compile and run the send.c code given at the end and then running
the qinfo code.</p>
<p>With all the knowledge that we have gained so far it is time
we started communicating with the queues, which is what they are used for.</p>
<H3>Sending and Receiving Messages</H3>
<p>In order to send and receive messages the UNIX based operating
systems provide two functions msgsnd() to send messages and msgrcv() to receive
messages. The definition of both the functions are as defined below.</p>
<pre>int msgsnd (int msqid, const void *msgp, size_t msgsz,
int msgflg);
int msgrcv (int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);</pre>
<P>
Let us first look at the msgsnd() function, it takes
4 parameters, the first one is the queue id of an existing queue. The 2
argument is the msgp or message pointer that contains the address of the of a
structure that holds the message and its type. This structure is described
below.
<PRE>
struct message{
long mtype; //The message type.
char mesg [MSGSZ];//The message is of length MSGSZ.
};
</PRE>
<p>The 3 parameter MSGSZ is the length of the message sent in
bytes. The final parameter msgflg specifies the action to be taken if one or
more of the following are true.</p>
<ul>
<li>The number of bytes already in the queue is equal to
msg_qbytes. This value is stored in the queue structure msqid_ds.</li>
<li>The total number of messages on all queues on the system
has reached a maximum limit that is imposed by the system.</li>
</ul>
<p>What are the actions to be taken in each of these cases?</p>
<ul>
<li>If (<tt>msgflg
&amp; IPC_NOWAIT</tt>) is
non-zero, the message will not be sent and the calling process will return
immediately. </li>
<li>If (<tt>msgflg
&amp; IPC_NOWAIT</tt>) is 0,
the calling process will suspend execution until one of the following
occurs:
<ul>
<li>The condition responsible for
the suspension no longer exists, in which case the message is sent.
</li>
<li>The message queue identifier
<tt>msqid</tt>
is removed from the system; when this occurs, <tt>
errno</tt>
is set equal to <tt>EIDRM</tt>
and -1 is returned. </li>
<li>The calling process receives a
signal that is to be caught; in this case the message is not sent and the
calling process resumes execution. </li>
</ul>
<p>Upon successful completion, the
following actions are taken with respect to the data structure associated
with <tt>msqid</tt>:
<ul>
<li><tt>msg_qnum</tt>
is incremented by 1 i.e. the count of number of messages in the queue&nbsp;
is incremented by 1.</li>
<li><tt>msg_lspid</tt>
is set equal to the process ID of the calling process. The msg_lspid field
contains the pid of the process that last accessed the queue. </li>
<li><tt>msg_stime</tt>
is set equal to the current time. The msg_stime field holds the time the
last message was sent to the queue.</li>
</ul>
</li>
</ul>
<p>The above fields are elements of&nbsp;
the msqid_ds structure. The msgrcv() function has an additional parameter in
msgtype which is the received message's type as specified by the sending
process. Only messages with matching priorities will be printed on the screen.
Further explanation is provided in recv.c. </p>
<p>The ensuing programs will give you a perfect idea of what we
have been talking till now. The code below presents the idea of message passing
between 2 processes. send.c is the code that creates a message queue and puts a
message into it. recv.c reads that message from the queue.</p>
<p><a href="misc/raghu/send.c.txt">send.c</a></p>
<p><a href="misc/raghu/recv.c.txt">recv.c</a></p>
<p>How does send.c work?</p>
<p>The code begins by defining a structure msgbuf that will hold
the message to be put in the queue. It contains 2 fields as explained earlier,
the type field mtype and the message that is stored in an array mtext. A queue
is then created using the msget function with a key value 10 and the flag
parameter being IPC_CREAT|0666 whereby we give read permission to all users. We
give a priority of 1 to the message by setting the mtype field as 1. We then
copy the text &quot;I am in the queue&quot; into the array mtext which is our message
array. We are ready to send the message to the queue that we just created
by invoking the msgsnd() function with the IPC_NOWAIT option (check above for
the explanation of the function). At each stage of a function call we check for
errors using the perror() function.</p>
<p>&nbsp;Now recv.c.</p>
<p>This is a straightforward code. This code too begins by
defining a structure that will hold the message obtained from the queue. The
code proceeds by creating a queue with the key value 10, if it already exists
then the queue-id is obtained. A point to be noted here is that only those
processes having the same key value as the one we had created in send.c can
access the queue. You can draw analogy to&nbsp; two people holding the key for
the same lock. If one them locks it only he or the other person can open it, no
one else can (you can of course break it open!). The msgrcv() function then
acquires the message from the queue into rbuf which is then subsequently printed
out. The fourth argument in msgrcv() is 1, could you figure out why? As
explained earlier the program send.c had sent the message with priority 1, in
order for the message in queue to be displayed on screen when recv.c is run it
should have matching priority, this explains the reason why 1 is passed as the
fourth parameter. The fifth parameter msgflag is 0 just ignore it (i say that
because that is what is done) or you could do it the right way by specifying it
to be IPC_NOWAIT|MSG_NOERROR , with this flag the receiver ignores the error
that might be caused due to the inconsistency in the&nbsp; length of the message
received and the length parameter passed. If the received message is of greater length
than MSGSZ an error is reported if MSG_NOERROR is not used. Try the ipcs command
after running&nbsp; send.c and later on running recv.c. The outputs will
be similar to ones shown below.</p>
<p>After send.c:</p>
<PRE>
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems status
------ Message Queues --------
key msqid owner perms used-bytes messages
0x0000000a 65536 root 666 19 1
</PRE>
After recv.c:
<PRE>
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems status
------Message Queues --------
key msqid owner perms used-bytes messages
0x0000000a 65536 root 666 0 0
</PRE>
<p>Notice the difference in the fields used-bytes and messages.
The message filled into the queue 10 by send.c was consumed by recv.c.</p>
<p>A good variation that you might try out is to check the effect
of negative priority values. Modify send.c by entering into the queue more than
one message (say 3) with priorities set to 1,2 and 3. Also modify recv.c by
setting the priority field to -2 and later -3. What happened? By letting the
priority field to be a negative value say -n the recv.c displays all the
messages starting from priority 1,2,3.....n. Why do we need it? Well if you set n to be a
very large number say 1000,we could get the queue emptied. </p>
<p>In future issues we will explore more complex
applications of message queues.</p>
<!-- *** BEGIN author bio *** -->
<P>&nbsp;
<P>
<!-- *** BEGIN bio *** -->
<P>
<img ALIGN="LEFT" ALT="[BIO]" SRC="../gx/2002/note.png">
<em>
I am a final year student doing my Btech in Computer Science
and Engineering at Government Engineering College Trichur, Kerala, India. For me
knowledge is a ceaseless quest for truth.
</em>
<br CLEAR="all">
<!-- *** END bio *** -->
<!-- *** END author bio *** -->
<!-- *** BEGIN copyright *** -->
<hr>
<CENTER><SMALL><STRONG>
Copyright &copy; 2003, Raghu J Menon.
Copying license <A HREF="../copying.html">http://www.linuxgazette.com/copying.html</A><BR>
Published in Issue 89 of <i>Linux Gazette</i>, April 2003
</STRONG></SMALL></CENTER>
<!-- *** END copyright *** -->
<HR>
<!--startcut ==========================================================-->
<CENTER>
<!-- *** BEGIN navbar *** -->
<!-- *** END navbar *** -->
</CENTER>
</BODY></HTML>
<!--endcut ============================================================-->