.. _ShMem:
.. raw:: html
.. |--| unicode:: U+2013 .. en dash
.. |---| unicode:: U+2014 .. em dash, trimming surrounding whitespace
:trim:
.. This file is part of the OpenCSF eTextbook project. It was
.. auto-generated by scripts from the OpenDSA eTextbook project.
.. See https://OpenCSF.org for more details. OpenCSF is distributed
.. under a Creative Commons Attribution-NonCommercial 4.0 International
.. License (see http://creativecommons.org/licenses/by-nc/4.0/),
.. Copyright (c) 2019-2021 by Michael S. Kirkpatrick. OpenDSA is
.. distributed under an MIT open source license, Copyright (c) 2012-2021
.. by the OpenDSA Project Contributors.
.. avmetadata::
:author: Michael S. Kirkpatrick
:requires: IPC Models
:satisfies: Shared Memory
:topic:
Shared Memory
=============
.. _IPCShmem:
.. figure:: Images/CSF-Images.3.6.png
:align: right
:figwidth: 40%
:width: 90%
:alt: A shared memory region present in two processes
A shared memory region present in two processes
Setting up :term:`shared memory` with the techniques in this section is very similar to using
:term:`memory-mapped files `, with the exception that there is no pre-defined
persistent file. That is, shared memory can be set up to allow for immediate data exchange between
processes without having a persistent record of the communication. :num:`Figure #IPCShmem`
illustrates the logical structure of a shared memory region that is mapped into two different processes.
For applications that exchange large amounts of data, shared memory is far superior to
message-passing techniques like message queues, which require system calls for every data exchange.
The major disadvantage of shared memory is that the processes must take extra precaution to
synchronize access to the region. If process A writes into the shared region, that might cause
unstable behavior in process B, or vice versa.
.. topic:: Bug Warning
.. figure:: Images/CSF-Images-BugWarning.png
:align: left
:width: 90%
:alt: Decorative bug warning
When a shared memory region is established in two or more processes, there is no guarantee that the
regions will be placed at the same base address. For instance, one process might have the shared
region starting at address ``0x40000000`` while the other process uses ``0x50008000``. It is
critical to understand that these two addresses refer to the exact same piece of data. So storing
the number 1 in the first process's address ``0x40000000`` means the second process has the value
of 1 at ``0x50008000``. The two (different) addresses refer to the exact same location.
Given that the base addresses are different, all elements in the region must also have different
addresses. The implication of this is that shared memory regions cannot use pointers to refer to
other parts of the region. For instance, assume the shared memory contains a linked list with the
first node at the beginning of the region. The next node appears 256 bytes later. Using the
addresses above, in one process, ``0x40000000`` must contain a pointer to address ``0x40000100``;
in the other address, the pointer must point to ``0x50008100``, given the different base address.
This means that the beginning of the shared memory region must simultaneously store `two different`
values, which is impossible.
The solution is to avoid the use of pointers within the shared memory region, using pointer
arithmetic instead. That is, if ``char *baseaddr`` is declared to point to the base address of the
region, then the two processes would need to use ``*(baseaddr + 256)`` to refer to the next node of
the linked list. Or, as an alternative, avoid linked lists entirely and use a contiguous data
structure instead.
POSIX Shared Memory
-------------------
The POSIX interface for shared memory is very simple, consisting of two primary functions. The
``shm_open()`` takes the POSIX IPC object name, a bit-mask of flags (``oflag``) and a permission
mode to apply new objects. [#f18]_ Similarly, ``shm_unlink()`` deletes the shared memory object.
.. topic:: C library functions –
.. figure:: Images/CSF-Images-Library.png
:align: left
:width: 100%
:alt: Decorative C library image
``int shm_open (const char *name, int oflag, mode_t mode);``
Open a connection to a POSIX shared memory object.
``int shm_unlink (const char *name);``
Delete a POSIX shared memory object.
The following code sample uses a ``struct`` declared as follows:
.. codeinclude:: IPC/ShmPermission.c
:linenos: true
`Code Listing 3.10 <#cl3-10>`_ sets up a POSIX shared memory object, which is identified by a given name. Note
that the call to ``shm_open()`` creates the object but does not specify a size; instead,
``ftruncate()`` resizes the object to be large enough to store one instance of the ``struct``
permission. Next, the process maps the shared memory object into memory with ``mmap()`` before using
``fork()`` to create a child process (which inherits both the shared memory identifier and the
memory mapped region). The child writes to the region before exiting; the parent waits until the
child exits and reads the data.
.. _cl3-10:
.. codeinclude:: IPC/CodeListing-3.10.c
:linenos: true
Combining ``shm_open()`` and ``mmap()`` in this way is a common technique that often confuses
novices. Specifically, it seems redundant to use both IPC mechanisms, because the same task could be
accomplished with either ``shm_open()`` or ``mmap()`` (not both). While it is true that both are not
required, combining them leads to certain advantages:
* The ``shm_open()`` parameters adhere to the standard POSIX IPC conventions. This allows the
processes to use POSIX names to identify the objects, rather than creating files in arbitrary locations.
* Using ``shm_unlink()`` provides a safety check that is not guaranteed with creating and mapping
arbitrary files. Specifically, ``shm_unlink()`` will not delete the object immediately if any other
process also has an open connection; this delay allows decreases the likelihood of other processes
experiencing random and unexpected failures. In contrast, if an arbitrary non-IPC file was used for
the ``mmap()``, the timing of the deletion would be determined by the underlying file system; this
introduces unnecessary maintenance and possibly restricts the portability of the program.
* Using ``mmap()`` allows the developer to cast the shared memory object to a more meaningful data
structure type and to avoid using file operations. Specifically, observe that the return type from
``shm_open()`` is a file descriptor, which does not provide any information about the type of data
stored in the object. By mapping it into memory, we can cast the pointer returned and access the
``user``, ``group``, and ``other`` fields contained in the ``struct``.
.. [#f18] As a minor point of terminology, all forms of POSIX IPC, including shared memory, are
referred to as *objects*. In contrast, System V shared memory is called a "segment" for historical purposes.
.. avembed:: Exercises/IPC/IPCShmSumm.html ka
:module: ShMem
:long_name: IPC shared memory questions
:threshold: 2