Exchanging Messages

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);

Note

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.

Note

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.

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);

Note

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:

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