445 lines
20 KiB
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 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> ------ 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 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> </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 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. 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
|
|
msqid owner perms
|
|
used-bytes messages <br>
|
|
0x0000000a 0
|
|
root 666
|
|
0
|
|
0 <br>
|
|
<br>
|
|
Okay here is an exercise for you, try replacing the flag
|
|
variable with 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
|
|
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 <id-number>.
|
|
</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 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 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. </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>
|
|
</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 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.
|
|
<br>
|
|
</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 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> 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
|
|
& IPC_NOWAIT</tt>) is
|
|
non-zero, the message will not be sent and the calling process will return
|
|
immediately. </li>
|
|
<li>If (<tt>msgflg
|
|
& 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
|
|
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
|
|
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 "I am in the queue" 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> 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 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 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 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>
|
|
<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 © 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 ============================================================-->
|