The msgsnd() and msgrcv() system calls perform I/O on message queues. The first argument to both system calls (msqid) is a message queue identifier. The second argument, msgp, is a pointer to a programmer-defined structure used to hold the message being sent or received. This structure has the following general form:
struct mymsg { long mtype; /* Message type */ char mtext[]; /* Message body */ }
This definition is really just shorthand for saying that the first part of a message contains the message type, specified as a long integer, while the remainder of the message is a programmer-defined structure of arbitrary length and content; it need not be an array of characters. Thus, the mgsp argument is typed as void * to allow it to be a pointer to any type of structure.
A zero-length mtext field is permitted, and is sometimes useful if the information to be conveyed can be encoded solely in the message type or if the existence of a message is in itself sufficient information for the receiving process.
The msgsnd() system call writes a message to a message queue.
#include <sys/types.h> /* For portability */
#include <sys/msg.h>
int msgsnd
(int msqid, const void *msgp, size_t msgsz, int msgflg);
Returns 0 on success, or -1 on error
To send a message with msgsnd(), we must set the mtype field of the message structure to a value greater than 0 (we see how this value is used when we discuss msgrcv() in the next section) and copy the desired information into the programmer-defined mtext field. The msgsz argument specifies the number of bytes contained in the mtext field.
When sending messages with msgsnd(), there is no concept of a partial write as with write(). This is why a successful msgsnd() needs only to return 0, rather than the number of bytes sent.
The final argument, msgflg, is a bit mask of flags controlling the operation of msgsnd(). Only one such flag is defined:
A msgsnd() call that is blocked because the queue is full may be interrupted by a signal handler. In this case, msgsnd() always fails with the error EINTR
. (As noted in Interruption and Restarting of System Calls, msgsnd() is among those system calls that are never automatically restarted, regardless of the setting of the SA_RESTART
flag when the signal handler is established.)
Writing a message to a message queue requires write permission on the queue.
The program in Example 46-2 provides a command-line interface to the msgsnd() system call. The command-line format accepted by this program is shown in the usageError() function. Note that this program doesn’t use the msgget() system call. (We noted that a process doesn’t need to use a get call in order to access an IPC object in Section 45.1.) Instead, we specify the message queue by providing its identifier as a command-line argument. We demonstrate the use of this program in Receiving Messages.
Example 46-2. Using msgsnd() to send a message
svmsg/svmsg_send.c
#include <sys/types.h> #include <sys/msg.h> #include "tlpi_hdr.h" #define MAX_MTEXT 1024 struct mbuf { long mtype; /* Message type */ char mtext[MAX_MTEXT]; /* Message body */ }; static void /* Print (optional) message, then usage description */ usageError(const char *progName, const char *msg) { if (msg != NULL) fprintf(stderr, "%s", msg); fprintf(stderr, "Usage: %s [-n] msqid msg-type [msg-text]\n", progName); fprintf(stderr, " -n Use IPC_NOWAIT flag\n"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int msqid, flags, msgLen; struct mbuf msg; /* Message buffer for msgsnd() */ int opt; /* Option character from getopt() */ /* Parse command-line options and arguments */ flags = 0; while ((opt = getopt(argc, argv, "n")) != -1) { if (opt == 'n') flags |= IPC_NOWAIT; else usageError(argv[0], NULL); } if (argc < optind + 2 || argc > optind + 3) usageError(argv[0], "Wrong number of arguments\n"); msqid = getInt(argv[optind], 0, "msqid"); msg.mtype = getInt(argv[optind + 1], 0, "msg-type"); if (argc > optind + 2) { /* 'msg-text' was supplied */ msgLen = strlen(argv[optind + 2]) + 1; if (msgLen > MAX_MTEXT) cmdLineErr("msg-text too long (max: %d characters)\n", MAX_MTEXT); memcpy(msg.mtext, argv[optind + 2], msgLen); } else { /* No 'msg-text' ==> zero-length msg */ msgLen = 0; } /* Send message */ if (msgsnd(msqid, &msg, msgLen, flags) == -1) errExit("msgsnd"); exit(EXIT_SUCCESS); }svmsg/svmsg_send.c
The msgrcv() system call reads (and removes) a message from a message queue, and copies its contents into the buffer pointed to by msgp.
#include <sys/types.h> /* For portability */
#include <sys/msg.h>
ssize_t msgrcv
(int msqid, void *msgp, size_t maxmsgsz, long
msgtyp, int msgflg);
Returns number of bytes copied into mtext field, or -1 on error
The maximum space available in the mtext field of the msgp buffer is specified by the argument maxmsgsz. If the body of the message to be removed from the queue exceeds maxmsgsz bytes, then no message is removed from the queue, and msgrcv() fails with the error E2BIG
. (This default behavior can be changed using the MSG_NOERROR
flag described shortly.)
Messages need not be read in the order in which they were sent. Instead, we can select messages according to the value in the mtype field. This selection is controlled by the msgtyp argument, as follows:
If msgtyp equals 0, the first message from the queue is removed and returned to the calling process.
If msgtyp is greater than 0, the first message in the queue whose mtype equals msgtyp is removed and returned to the calling process. By specifying different values for msgtyp, multiple processes can read from a message queue without racing to read the same messages. One useful technique is to have each process select messages matching its process ID.
If msgtyp is less than 0, treat the waiting messages as a priority queue. The first message of the lowest mtype less than or equal to the absolute value of msgtyp is removed and returned to the calling process.
An example helps clarify the behavior when msgtyp is less than 0. Suppose that we have a message queue containing the sequence of messages shown in Figure 46-1 and we performed a series of msgrcv() calls of the following form:
msgrcv(id, &msg, maxmsgsz, -300, 0);
These msgrcv() calls would retrieve messages in the order 2 (type 100), 5 (type 100), 3 (type 200), and 1 (type 300). A further call would block, since the type of the remaining message (400) exceeds 300.
The msgflg argument is a bit mask formed by ORing together zero or more of the following flags:
IPC_NOWAIT
Perform a nonblocking receive. Normally, if no message matching msgtyp is in the queue, msgrcv() blocks until such a message becomes available. Specifying the IPC_NOWAIT
flag causes msgrcv() to instead return immediately with the error ENOMSG
. (The error EAGAIN
would be more consistent, as occurs on a nonblocking msgsnd() or a nonblocking read from a FIFO. However, failing with ENOMSG
is historical behavior, and required by SUSv3.)
MSG_EXCEPT
This flag has an effect only if msgtyp is greater than 0, in which case it forces the complement of the usual operation; that is, the first message from the queue whose mtype is not equal to msgtyp is removed from the queue and returned to the caller. This flag is Linux-specific, and is made available from <sys/msg.h>
only if _GNU_SOURCE
is defined. Performing a series of calls of the form msgrcv(id, &msg, maxmsgsz, 100, MSG_EXCEPT) on the message queue shown in Figure 46-1 would retrieve messages in the order 1, 3, 4, and then block.
MSG_NOERROR
By default, if the size of the mtext field of the message exceeds the space available (as defined by the maxmsgsz argument), msgrcv() fails. If the MSG_NOERROR
flag is specified, then msgrcv() instead removes the message from the queue, truncates its mtext field to maxmsgsz bytes, and returns it to the caller. The truncated data is lost.
Upon successful completion, msgrcv() returns the size of the mtext field of the received message; on error, -1 is returned.
As with msgsnd(), if a blocked msgrcv() call is interrupted by a signal handler, then the call fails with the error EINTR
, regardless of the setting of the SA_RESTART
flag when the signal handler was established.
Reading a message from a message queue requires read permission on the queue.
The program in Example 46-3 provides a command-line interface to the msgrcv() system call. The command-line format accepted by this program is shown in the usageError() function. Like the program in Example 46-2, which demonstrated the use of msgsnd(), this program doesn’t use the msgget() system call, but instead expects a message queue identifier as its command-line argument.
The following shell session demonstrates the use of the programs in Example 46-1, Example 46-2, and Example 46-3. We begin by creating a message queue using the IPC_PRIVATE
key, and then write three messages with different types to the queue:
$./svmsg_create -p
32769 ID of message queue $./svmsg_send 32769 20 "I hear and I forget."
$./svmsg_send 32769 10 "I see and I remember."
$./svmsg_send 32769 30 "I do and I understand."
We then use the program in Example 46-3 to read messages with a type less than or equal to 20 from the queue:
$./svmsg_receive -t -20 32769
Received: type=10; length=22; body=I see and I remember. $./svmsg_receive -t -20 32769
Received: type=20; length=21; body=I hear and I forget. $./svmsg_receive -t -20 32769
The last of the above commands blocked, because there was no message in the queue whose type was less than or equal to 20. So, we continue by typing Control-C to terminate the command, and then execute a command that reads a message of any type from the queue:
Type Control-C to terminate program
$ ./svmsg_receive 32769
Received: type=30; length=23; body=I do and I understand.
Example 46-3. Using msgrcv() to read a message
svmsg/svmsg_receive.c
#define _GNU_SOURCE /* Get definition of MSG_EXCEPT */ #include <sys/types.h> #include <sys/msg.h> #include "tlpi_hdr.h" #define MAX_MTEXT 1024 struct mbuf { long mtype; /* Message type */ char mtext[MAX_MTEXT]; /* Message body */ }; static void usageError(const char *progName, const char *msg) { if (msg != NULL) fprintf(stderr, "%s", msg); fprintf(stderr, "Usage: %s [options] msqid [max-bytes]\n", progName); fprintf(stderr, "Permitted options are:\n"); fprintf(stderr, " -e Use MSG_NOERROR flag\n"); fprintf(stderr, " -t type Select message of given type\n"); fprintf(stderr, " -n Use IPC_NOWAIT flag\n"); #ifdef MSG_EXCEPT fprintf(stderr, " -x Use MSG_EXCEPT flag\n"); #endif exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int msqid, flags, type; ssize_t msgLen; size_t maxBytes; struct mbuf msg; /* Message buffer for msgrcv() */ int opt; /* Option character from getopt() */ /* Parse command-line options and arguments */ flags = 0; type = 0; while ((opt = getopt(argc, argv, "ent:x")) != -1) { switch (opt) { case 'e': flags |= MSG_NOERROR; break; case 'n': flags |= IPC_NOWAIT; break; case 't': type = atoi(optarg); break; #ifdef MSG_EXCEPT case 'x': flags |= MSG_EXCEPT; break; #endif default: usageError(argv[0], NULL); } } if (argc < optind + 1 || argc > optind + 2) usageError(argv[0], "Wrong number of arguments\n"); msqid = getInt(argv[optind], 0, "msqid"); maxBytes = (argc > optind + 1) ? getInt(argv[optind + 1], 0, "max-bytes") : MAX_MTEXT; /* Get message and display on stdout */ msgLen = msgrcv(msqid, &msg, maxBytes, type, flags); if (msgLen == -1) errExit("msgrcv"); printf("Received: type=%ld; length=%ld", msg.mtype, (long) msgLen); if (msgLen > 0) printf("; body=%s", msg.mtext); printf("\n"); exit(EXIT_SUCCESS); }svmsg/svmsg_receive.c