msgget()
In order to use a message queue, it has to be created first. The
msgget()
system call is used to do just that. This system call
accepts two parameters - a queue key, and flags. The key may be one of:
open()
system call,
and will be explained later, and it also contains access permission bits.
The lowest 9 bits of the flags are used to define access permission for
the queue, much like similar 9 bits are used to control access to files.
the bits are separated into 3 groups - user, group and others. In each set,
the first bit refers to read permission, the second bit - to write permission,
and the third bit is ignored (no execute permission is relevant to message
queues).
#include <stdio.h> /* standard I/O routines. */
#include <sys/types.h> /* standard system data types. */
#include <sys/ipc.h> /* common system V IPC structures. */
#include <sys/msg.h> /* message-queue specific functions. */
/* create a private message queue, with access only to the owner. */
int queue_id = msgget(IPC_PRIVATE, 0600); /* <-- this is an octal number. */
if (queue_id == -1) {
perror("msgget");
exit(1);
}
A few notes about this code:
struct msgbuf
Before we go to writing messages to the queue or reading messages from it,
we need to see how a message looks. The system defines a structure named
'msgbuf' for this purpose. Here is how it is defined:
struct msgbuf {
long mtype; /* message type, a positive number (cannot be zero). */
char mtext[1]; /* message body array. usually larger then one byte. */
};
sizeof(struct msgbuf)
. Lets see how we create
an "hello world" message:
/* first, define the message string */
char* msg_text = "hello world";
/* allocate a message with enough space for length of string and */
/* one extra byte for the terminating null character. */
struct msgbuf* msg =
(struct msgbuf*)malloc(sizeof(struct msgbuf) + strlen(msg_text));
/* set the message type. for example - set it to '1'. */
msg->mtype = 1;
/* finally, place the "hello world" string inside the message. */
strcpy(msg->mtext, msg_text);
Few notes:
strlen(msg_text)
more then the size
of "struct msgbuf
", and didn't need to allocate an extra
place for the null character, cause that's already contained in the
msgbuf structure (the 1 byte of mtext there).
memset()
, and not strcpy()
.
msgsnd()
Once we created the message queue, and a message structure, we can place it on the message queue, using the msgsnd() system call. This system call copies our message structure and places that as the last message on the queue. It takes the following parameters:
int msqid
- id of message queue, as returned from
the msgget()
call.
struct msgbuf* msg
- a pointer to a properly
initializes message structure, such as the one we prepared in the
previous section.
int msgsz
- the size of the data part (mtext) of
the message, in bytes.
int msgflg
- flags specifying how to send the message:
IPC_NOWAIT
- if the message cannot be sent
immediately, without blocking the process, return '-1', and set
errno
to EAGAIN
.
msgsnd()
like this:
int rc = msgsnd(queue_id, msg, strlen(msg_text)+1, 0);
if (rc == -1) {
perror("msgsnd");
exit(1);
}
msgsnd()
assumes
the data in the message to be an arbitrary sequence of bytes, so it cannot
know we've got the null character there too, unless we state that explicitly.
msgrcv()
We may use the system call msgrcv()
In order to read a message
from a message queue. This system call accepts the following list of
parameters:
int msqid
- id of the queue, as returned from
msgget()
.
struct msgbuf* msg
- a pointer to a pre-allocated
msgbuf structure. It should generally be large enough to contain a
message with some arbitrary data (see more below).
int msgsz
- size of largest message text we wish
to receive. Must NOT be larger then the amount of space we allocated
for the message text in 'msg'.
int msgtyp
- Type of message we wish to read.
may be one of:
int msgflg
- a logical 'or' combination of any
of the following flags:
IPC_NOWAIT
- if there is no message on the queue
matching what we want to read, return '-1', and set
errno
to ENOMSG
.
MSG_EXCEPT
- if the message type parameter
is a positive integer, then return the first message whose type
is NOT equal to the given integer.
MSG_NOERROR
- If a message with a text part
larger then 'msgsz' matches what we want to read, then truncate
the text when copying the message to our msgbuf structure.
If this flag is not set and the message text is too large,
the system call returns '-1', and errno
is set
to E2BIG
.
/* prepare a message structure large enough to read our "hello world". */
struct msgbuf* recv_msg =
(struct msgbuf*)malloc(sizeof(struct msgbuf)+strlen("hello world"));
/* use msgrcv() to read the message. We agree to get any type, and thus */
/* use '0' in the message type parameter, and use no flags (0). */
int rc = msgrcv(queue_id, recv_msg, strlen("hello world")+1, 0, 0);
if (rc == -1) {
perror("msgrcv");
exit(1);
}
A few notes:
msgrcv()
call
would have blocked our process until one of the following happens:
errno
would be set
to EIDRM
).
errno
would
be set to EINTR
.
As an example of using non-private message queues, we will show a program, named "queue_sender", that creates a message queue, and then starts sending messages with different priorities onto the queue. A second program, named "queue_reader", may be run that reads the messages from the queue, and does something with them (in our example - just prints their contents to standard output). The "queue_reader" is given a number on its command line, which is the priority of messages that it should read. By running several copies of this program simultaneously, we can achieve a basic level of concurrency. Such a mechanism may be used by a system in which several clients may be sending requests of different types, that need to be handled differently.
Also see man msgctl.html