NAME
mq,
mqueue —
POSIX
message queues (REALTIME)
LIBRARY
POSIX Real-time Library (librt, -lrt)
SYNOPSIS
#include <mqueue.h>
DESCRIPTION
The
IEEE Std 1003.1-2001 (“POSIX.1”)
standard defines and
NetBSD implements an interprocess
communication (IPC) interface known as POSIX message queues. Although the
basic functionality is similar,
mq is distinct from the
older
AT&T System V UNIX message queues (see
for example
ipcs(1) or
msgget(2)).
Rationale
The rationale behind
mq is to provide an efficient,
priority-driven asynchronous IPC mechanism. When the
AT&T
System V UNIX message queues were first implemented, the reasoning
was similar: the only form of IPC was half-duplex pipes and message queues
were seen to overcome the performance limitations with these.
But arguably in modern systems there is little difference between the efficiency
of the System V message queues, pipes, and UNIX domain sockets (if anything,
the
AT&T System V UNIX message queues tend to
be slower than the rest). The fundamental performance bottleneck is however
still there with
mq as well: data must be first copied from
the sender to the kernel and then from the kernel to the receiver. The bigger
the message, the higher the overhead.
For realtime applications,
mq offers some advantages:
- Unlike the predecessors, mq provides an
asynchronous notification mechanism.
- Messages are prioritized. The queue always remains sorted
such that the oldest message of the highest priority is always received
first, regardless of the number of messages in the queue.
- By default, the functions to send and receive messages are
blocking calls. It is however possible to use non-blocking variants with
mq. Furthermore, it is possible to specify timeouts to
avoid non-deterministic blocking.
- Resource limits can be enforced -- or perhaps more
importantly, the availability of resources can be ensured as the internal
data structures are preallocated.
Descriptors and Naming
Comparable to pipes and FIFOs (a.k.a. named pipes), all POSIX message queue
operations are performed by using a descriptor. The used type is
mqd_t, an abbreviation from a “message queue
descriptor”. In the
NetBSD implementation this
is actually an ordinary file descriptor. This means that it is possible, but
not portable, to monitor a message queue descriptor by using
poll(2) or
select(2).
Message queues are named by character strings that represent (absolute)
pathnames. The used interface is analogous to the conventional file concepts.
But unlike FIFOs and pipes, neither POSIX nor System V message queues are
accessed by using
open(2),
read(2), or
write(2). Instead, equivalents
such as
mq_open(),
mq_close(), and
mq_unlink() are used.
The standard does not specify whether POSIX message queues are exposed at the
file system level. It can be argued that
mq inherited an old
problem with the System V message queues. Even if an implementation would have
support for it, it is not portable to view message queues by
ls(1), remove these with
rm(1), or adjust the permissions
with
chmod(1).
Processes
When a new process is created or the program is terminated, message queues
behave like files. More specifically, when
fork(2) is called, files and
message queues are inherited, and when the program terminates by calling
exit(3) or
_exit(2), both file descriptors
and message queues are closed. However, the
exec(3) family of functions behave
somewhat differently for message queues and files: all message queues are
closed when a process calls one of the
exec() functions. In
this respect POSIX message queues are closer to FIFOs than normal pipes.
Attributes
All message queues have an attribute associated with them. This is represented
by the
mq_attr structure:
struct mq_attr {
long mq_flags;
long mq_maxmsg;
long mq_msgsize;
long mq_curmsgs;
};
The members in the structure are: flags set for the message queue
(
mq_flags), the maximum number of messages in the queue
(
mq_maxmsg), the maximum size of each message
(
mq_msgsize), and the number of queued messages
(
mq_curmsgs).
The overall resource requirements for a particular message queue are given by
mq_maxmsg and
mq_msgsize. These
two can be specified when the queue is created by a call to
mq_open(). The constraints are enforced through the lifetime
of the queue: an error is returned if a message larger than
mq_msgsize is sent, and if the message queue is already
full, as determined by
mq_maxmsg, the call to queue a
message will either block or error out.
Although there are two functions,
mq_getattr() and
mq_setattr(), to retrieve and set attributes, resource
limits cannot be changed once the queue has been created. In
NetBSD the super user may however control the global
resource limits by using few
sysctl(7) variables.
Asynchronous Notification
Instead of blocking in the functions that receive messages,
mq
offers an asynchronous mechanism for a process to receive notifications that
messages are available in the message queue. The function
mq_notify() is used to register for notification. Either a
signal or a thread can be used as the type of notification; see
sigevent(3) for details.
Bear in mind that no notification is sent for an arrival of a message to a
non-empty message queue. In other words,
mq_notify() does
not by itself ensure that a process will be notified every time a message
arrives. Thus, after having called
mq_notify(), an
application may need to repeatedly call
mq_receive() until
the queue is empty. This requires that the message queue was created with the
O_NONBLOCK
flag; otherwise
mq_receive() blocks until a message is again queued or the
call is interrupted by a signal. This may be a limitation for some realtime
applications.
Priorities
Each message has a priority, ranging from 0 to the implementation-defined
MQ_PRIO_MAX
. The POSIX standard enforces the minimum
value of the maximum priority to be 32. All messages are inserted into a
message queue according to the specified priority. High priority messages are
sent before low priority messages. If the used priority is constant,
mq follows the FIFO (First In, First Out) principle.
The basic rule of thumb with realtime prioritization is that low priority tasks
should never unnecessarily delay high priority tasks. Priority inheritance is
not however part of the provided API; the receiver process may run at low
priority even when receiving high priority messages. To address this
limitation and other potential realtime problems, the user may consider other
functions from the
POSIX Real-time Library (librt,
-lrt). The process scheduling interface described in
sched(3) can be mentioned as an
example.
FUNCTIONS
The following functions are available in the API.
COMPATIBILITY
Despite of some early fears, the POSIX message queue implementations are fairly
compatible with each other. Nevertheless, few points can be noted for portable
applications.
- It is not portable to use functions external to the API
with message queue descriptors.
- The standard leaves the rules loose with respect to the
message queue names. Only the interpretation of the first slash character
is consistent; the following slash characters may or may not follow the
conventional construction rules for a pathname.
- The length limits for a message queue name are
implementation-defined. These may or may not follow the conventional
pathname limits
PATH_MAX
and
NAME_MAX
.
SEE ALSO
Bill O. Gallmeister,
POSIX.4: Programming for the Real World,
O'Reilly and Associates,
1995.
Richard W. Stevens,
UNIX Network Programming, Volume 2: Interprocess
Communications, Prentice Hall,
Second Edition, 1998.
STANDARDS
The POSIX message queue implementation is expected to conform to
IEEE Std 1003.1-2001 (“POSIX.1”).
HISTORY
The POSIX message queue API first appeared in
NetBSD
5.0.
CAVEATS
User should be careful to unlink message queues at the program termination.
Otherwise it is possible to leave them lying around.