Throughout this book, we try to focus on platform-independent concepts and code examples as much as possible. In particular, we tend to focus on the POSIX interface specification, given its broad base of support across the major modern OS. IPC is the exception that proves the rule. Some OS (e.g., macOS in particular) provide little or no support for POSIX IPC; instead, these OS uses the System V interface for IPC. In this section, we will continue to focus on POSIX and describe the relevant interfaces for message queues, semaphores, and shared memory. Appendix B describes the System V IPC interfaces for reference and comparison.
Bug Warning
POSIX IPC code will not work successfully on macOS. macOS does not provide the required header files for POSIX message queues, so the code will not successfully compile. Even worse, macOS provides the header files for POSIX unnamed semaphores (allowing the code to compile), but the implementation consists of empty stub functions. That is, the semaphore functions will simply return without actually acting like semaphores. To avoid these issues, code intended to run on macOS should use the System V IPC interface instead. Pre-processor directives can be used to switch between the code required at compile time.
All POSIX IPC functions identify and refer to IPC objects with a string that adheres to a given
format. The string must start with an initial slash, then have one or more non-slash characters. For
instance, /OpenCSF_mqueue
is a valid name, presumably for a message queue. Each object’s name
must be unique, so application designers need to ensure that any name they choose is unlikely to
conflict with other applications.
IPC objects are often represented as a file within a particular directory in the file system. In
Linux, information related to the /OpenCSF_mqueue
message queue would be stored in
/dev/mqueue/OpenCSF_mqueue
, for example. These files can be accessed using the standard file
commands, such as ls, cat, or rm. However, manipulating these files with these standard utilities
may cause unexpected behavior, as running processes may be relying on these files.
In addition to a name, all of the functions for opening a POSIX IPC connection accept at least two
other bit mask parameters: oflag
and mode
. The oflag
parameter specifies the requested
access needed (O_RDONLY
, O_WRONLY
, O_RDWR
); note that these are identical to the
standard options for opening a file with open()
. Similarly, the mode
parameter uses the
standard file permissions (e.g., S_IRUSR
, S_IWGRP
) to set the permissions on a newly created
object.
If a new object is being created, the O_CREAT
flag must be included in the oflag bit mask. If
O_CREAT
is omitted and the object does not exist, an error will be returned. In addition, the
O_EXCL
flag controls the behavior when attempting to create an object, but one with the
specified name already exists. If O_EXCL
is included and an object exists, the open fails and an
error is returned. If O_EXCL
is omitted, no object is created, but the open returns a connection
to the existing object. Note that passing O_EXCL
without O_CREAT
is undefined in the POSIX
specification and should not be used. The following examples illustrate how to use these options:
shm_open ("/shared_mem", O_CREAT | O_RDWR, S_IWUSR | S_IRGRP);
Create and/or open shared memory; if it doesn’t exist, set permissions to 0420 (user has write, group has read).shm_open ("/shared_mem", O_CREAT | O_EXCL, S_IWUSR | S_IRUSR);
Create but do not open shared memory with 0600 permissions. Fail if it already exists.shm_open ("/shared_mem", O_RDONLY);
Open a read-only connection to shared memory. If it does not exist, fail.