POSIX Message Queues: Two Implementations

As described in the Interprocess Communication (IPC) chapter of the System Architecture guide, QNX Neutrino supports POSIX message queues. The OS includes two managers for message queues:

mqueue
The traditional implementation.
mq
An alternate implementation.

The mq implementation uses a queue within the kernel to buffer the messages and eliminates the (context-switching) overheads of using an external server (i.e., mqueue) in each message-queue operation, thus improving performance.

Note:
  • Although the mq implementation provides better performance, it isn't as secure as the traditional implementation.
  • In QNX Neutrino 7.0 or later, mq and mqueue can coexist, but we recommend that you use only one of them on your system.

When you create a queue with mq, it appears in the pathname space under /dev/mq. Note that it's different from the pathname space for mqueue; the queue appears in the pathname space under /dev/mqueue. (You can change this directory to union over the directory exported by the mqueue server by using the mq -N/dev/mqueue option, but we don't recommend this, because it may cause some user-namespace confusion.)

Although the mq server isn't involved in each mq_send()/mq_receive()/mq_notify() operation, the server is necessary to maintain the queue names and create the corresponding kernel message queues. You can also use ls and rm for administrative purposes, but you can't manipulate the queue contents by using shell utilities.

The client functions communicate with mqueue or mq:

mq_open()
Open or create a queue
mq_close()
Close a queue
mq_unlink()
Remove a message queue
mq_getattr()
Get attributes on a queue
mq_setattr()
Set attributes on a queue
mq_notify()
Request notification when a message arrives
mq_send()
Send a message
mq_receive()
Receive a message

For more information about these functions, see the QNX Neutrino C Library Reference. The implementation of the mq_*() routines in libc is the traditional style, using the mqueue server to broker each transaction. In order to use the mq implementation, you must link your application(s) against the libmq library. In a manual build, specify the -l mq option; in automatic/recursive builds, use this setting in your common.mk file:

LIBS += mq
Note:
When relinking applications to use the alternative implementation, be sure to change all affected components. We require such explicit intervention to use the alternate implementation because of potential incompatibilities if your code isn't strictly conforming to POSIX.

Here are some other differences between these two servers:

  • Message queue descriptors (mqd_t) aren't file descriptors in the alternate implementation. The POSIX 1003.1 standard makes no claim as to the underlying type of an mqd_t, and provides a specific set of functions to manipulate a message queue and its attributes based on an abstract mqd_t type.

    For example, you should use mq_setattr() instead of fcntl() to modify the nonblocking attribute of an open message queue. See the following code:

    mq_getattr(mq, &attr);
    attr.mq_flags |= O_NONBLOCK;
    mq_setattr(mq, &attr, NULL);
    

    The following code attempts to obtain the number of messages in a queue (don't use fstat()):

    mq_getattr(mq, &attr);
    nmsg = attr.mq_curmsgs;
    
  • Historically, you could interchange mq_send() with write(), and mq_receive() with read(). This isn't possible with the alternate implementation.
  • In the alternate implementation, there's no direct counterpart to poll() when used on a mixed set of queues and file descriptors or for the transition from a full queue, although you can use mq_notify() to register an event against a message queue on transition from empty.
  • The alternate implementation doesn't support the DCMD_MISC_MQSETCLOSEMSG Neutrino extension to inject a canned message when a queue descriptor is closed.
  • In the traditional implementation, each call to mq_open() uses one file descriptor; in the alternate implementation, each call to mq_open() uses two.
  • The default configuration of a message queue created by mq_open(O_CREAT) with NULL for the mq_attr argument is different in the alternate implementation, because kernel buffers for the messages are created up front rather than on demand. To create a queue with a specific configuration, simply provide a non-NULL mq_attr argument. To force the NULL default to match that of the traditional implementation, use the mq -m1024 -s4096 options (which we don't recommend, because this consumes 4 MB of system memory for each such queue).
  • The alternate implementation allows for message queues to be manipulated only from the local machine.
  • In QNX Neutrino 7.0 or later, mq registers for files of type _FTYPE_MQ, and mqueue registers for files of type _FTYPE_MQUEUE.

The following table summarizes the main differences between the implementations:

Style Server Library Pathname mqd_t implementation Qnet File type
Traditional mqueue libc /dev/mqueue/ int (file descriptor) Yes _FTYPE_MQUEUE
Alternative mq libmq /dev/mq/ int (internal index) No _FTYPE_MQ
Page updated: