Shared memory and memory-mapped files allow processes to communicate by incorporating data directly into process address space. Processes communicate by sharing portions of their address space. When one process writes to a location in the shared area, the data is immediately available to other processes sharing the area. Communication is fast because there is none of the overhead associated with system calls. Data movement is reduced because it is not copied into buffers.
This chapter includes the following sections:
Memory Objects, Section 3.1
Locking Shared Memory, Section 3.2
Using Shared Memory with Semaphores, Section 3.3
A process manipulates its address space by mapping or removing portions of memory objects into the process address space. When multiple processes map the same memory object, they share access to the underlying data. Shared-memory functions allow you to open and unlink the shared-memory files.
The memory-mapping and shared-memory functions allow you controlled access to shared memory so that the application can coordinate the use of shared address space.
When you use a shared, mapped file, the changes initiated by a single process or multiple processes are reflected back to the file. Other processes using the same path and opening the connection to the memory object have a shared mapping of the file. Use memory-mapping or file control functions to control usage and access. If the mappings allow it, data written into the file through the address space of one process appears in the address space of all processes mapping the same portion of the file.
Memory-mapped objects are persistent; their names and contents remain until all processes that have accessed the object unlink the file.
Shared memory and memory-mapped files follow the same general usage, as follows:
Get a file descriptor with a call to the
open
or
shm_open
function.
Map the object using the file descriptor with a call to the
mmap
function.
Unmap the object with a call to the
munmap
function.
Close the object with a call to the
close
function.
Remove the shared-memory object with a call to the
shm_unlink
function or optionally remove a memory-mapped
file with a call to the
unlink
function.
Often shared-memory objects are created and used only while an application
is executing.
Files, however, may need to be saved and reused each time the
application is run.
The
unlink
and
shm_unlink
functions remove (delete) the file and its
contents.
Therefore, if you need to save a shared file, close the file but
do not unlink it.
You can use memory-mapped files without using shared memory, but this chapter assumes that you will want to use them together. The following functions are used to open and unlink shared memory:
Function | Description |
shm_open |
Opens a shared-memory object, returning a file descriptor |
shm_unlink |
Removes the name of the shared-memory object |
Table 3-1 lists the functions for creating and controlling memory-mapped objects.
Function | Description |
mmap |
Maps the memory object into memory |
mprotect |
Modifies protections of memory objects |
msync |
Synchronizes a memory-mapped object |
munmap |
Unmaps a previously mapped region |
A memory object can be created and opened by a call to the
shm_open
function.
Then the object can be mapped into
process address space.
File control functions allow you to control access
permissions, such as read and write permission or the timing of a file
update.
Data written to an object through the address space of one process is
available to all processes that map the same region.
Child processes inherit
the address space and all mapped regions of the parent process.
Once the
object is opened, the child process can map it with the
mmap
function to establish a map reference.
If the object
is already mapped, the child process also inherits the mapped region.
Unrelated processes can also use the object, but must first call the
open
or
shm_open
function (as
appropriate) and then use the
mmap
function to establish
a connection to the shared memory.
A process can create and open shared-memory regions early in the life of the
application and then dynamically control access to the shared-memory object.
Use the
shm_open
function to open (establish a connection
to) a shared-memory object.
After a process opens the shared-memory object,
each process that needs to use the shared-memory object must use the same
name as the controlling process when creating its own connections to the
shared-memory object by also calling the
shm_open
function.
The name can either be a string or a pathname, but in either
case, processes must use the same name to refer to a specific shared-memory
object.
The
shm_open
function provides a set of flags that
prescribe the action of the function and define access modes to the
shared-memory object.
Shared-memory access is determined by the OR of the
file status flags and access modes listed in
Table 3-2.
Flag | Description |
O_RDONLY | Open for read access only |
O_RDWR | Open for read and write access |
O_CREAT | Create the shared-memory object, if it does not already exist |
O_EXCL | Create an exclusive connection to a shared-memory object, when used with O_CREAT |
O_TRUNC | Truncate to zero length |
The first process to call the
shm_open
function should
use the O_CREAT flag to create the shared-memory object, to set the object's
user ID to that of the calling process, and to set the object's group ID to
the effective group ID of the calling process.
This establishes an
environment whereby the calling process, all cooperating processes, and
child processes share the same effective group ID with the shared-memory
object.
A process can create an exclusive connection to a shared-memory object by using the O_CREAT and O_EXCL flags. In this case, other processes attempting to create the shared-memory object at the same time will fail.
The
oflag
argument of the
shm_open
function requests specific actions from the
shm_open
code.
For example, the following code creates
an exclusive shared-memory object and opens it for read and write access.
fd = shm_open("all_mine", (O_CREAT|O_EXCL|O_RDWR), 0);
Once a shared-memory object is created, its state and name (including all
associated data) are persistent.
Its state and name remain until the shared
memory is unlinked with a call to the
shm_unlink
function
and until all other references to the shared memory are gone.
Example 3-1 shows the code sequence to include shared-memory objects in an application.
#include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <fcntl.h> main () { int md; int status; long pg_size; caddr_t virt_addr; /* Create shared memory object */ md = shm_open ("my_memory", O_CREAT|O_RDWR, 0); pg_size = sysconf(_SC_PAGE_SIZE); if((ftruncate(md, pg_size)) == -1){ /* Set the size */ perror("ftruncate failure"); exit(); } /* Map one page */ virt_addr = mmap(0, pg_size, PROT_WRITE, MAP_SHARED, md, 0);
.
.
.
status = munmap(virt_addr, pg_size); /* Unmap the page */ status = close(md); /* Close file */ status = shm_unlink("my_memory"); /* Unlink shared-memory object */ }
The
open
function points to the data you intend to use;
the
mmap
function establishes how much of the data will
be mapped and how it will be accessed.
Use the same access permissions that
you would normally use on any call to the
open
function.
If you intend to read the file only, specify read permission only on the
open
function.
If you intend to read and write to the
file, open the file with both read and write permission.
After opening a
file, call the
mmap
function to map the file into
application address space.
When you have finished using a memory-mapped file, unmap the object by
calling the
munmap
function, then close the object with
the
close
function.
Any memory locks resulting from a
call to the
mlock
function associated with the address
range are removed when the
munmap
function is called.
The
application could then remove the data file by calling the
unlink
function.
The
mmap
function maps data from a file into memory.
The
parameters to the
mmap
function specify the starting
address and length in bytes for the new region, access permissions,
attributes of the mapped region, file descriptor, and an offset for the
address.
The MAP_SHARED flag indicates the object will be accessible by
other processes.
A call to the
munmap
function unmaps the
same region.
The address, length, and offset of the new mapped region should be a
multiple of the page size returned by a call to the
sysconf(_SC_PAGE_SIZE)
function.
If the length is not
specified as a multiple of the page size returned by
sysconf
, then any reference to an address between the end
of the region and the end of the page containing the end of the region is
undefined.
Note, too, that the offset must be aligned and sized properly.
Other size parameters may also need to be aligned, depending on whether you
specified MAP_FIXED.
The prot argument determines the type of access permitted to the data being mapped. As with other file permissions, the argument is constructed from the bitwise inclusive-OR of one or more of the following flags:
Flag | Description |
PROT_READ | Data can be read |
PROT_WRITE | Data can be written |
PROT_EXEC | Data can be executed |
PROT_NONE | Data cannot be accessed |
Whatever protection options you specify as the prot argument, the file descriptor must have been opened with at least read access. If you specify PROT_WRITE, the file descriptor must have been opened with write permission, unless MAP_PRIVATE is specified in the flags parameter.
The flags parameter provides additional information about how to handle mapped data. The flags parameter uses one of the following flags:
Flag | Description |
MAP_SHARED | Share changes |
MAP_PRIVATE | Changes are private |
MAP_FIXED | Interpret the addr argument exactly |
MAP_SHARED, MAP_PRIVATE, and MAP_FIXED are the only flags specified by POSIX
1003.1b.
The MAP_ANONYMOUS, MAP_FILE, and MAP_VARIABLE flags are not part
of the POSIX 1003.1b interface, but are supported by DIGITAL UNIX.
For more
information on these flags, see the reference page for the
mmap
function.
The MAP_FIXED flag controls the location of the new region.
No matter what
flag is specified, a mapped region is never placed at address zero or at an
address where it would overlap with an existing region.
When multiple
processes use the mapped object, the call to the
mmap
function can specify the address, and subsequent calls to the
mmap
function can use MAP_FIXED to request the same
address in other processes.
Cooperating processes must also use care to
communicate this address among themselves.
If you specify MAP_FIXED and for
some reason the system is unable to place the new region at the specified
address, the call fails.
The MAP_SHARED and MAP_PRIVATE flags control the visibility of modifications to the mapped file or shared-memory region. The MAP_SHARED flag specifies that modifications made to the mapped file region are immediately visible to other processes which are mapped to the same region and also use the MAP_SHARED flag. Changes to the region are written to the file.
The MAP_PRIVATE flag specifies that modifications to the region are not visible to other processes whether or not the other process used MAP_SHARED or MAP_PRIVATE. Modifications to the region are not written to the file.
Access to the mapped region or shared-memory region is controlled by the
flags specified in the
prot
parameter.
These
flags function much the way they do for any other file descriptor: access is
specified as the OR of read, write, and execute, with an additional flag to
indicate that data cannot be accessed.
The
mprotect
function changes the protection on a specified address range.
That range
should be within the range specified on the call to the
mmap
function.
Protection flags can interact with the
MAP_SHARED, MAP_PRIVATE, and MAP_FIXED flags.
Refer to the online reference
pages for
mmap
and
mprotect
for
specifics.
When you unmap a mapped region or shared memory, be sure to specify an
address and length in the range of the parameters used in the call to the
mmap
function.
Shared-memory objects and memory-mapped files use the file system name space to map global names for memory objects. As such, POSIX.1 file control functions can be used on shared-memory objects and memory-mapped files, just as these functions are used for any other file control. Table 3-3 lists some of the file functions available.
Function | Description |
fchmod |
Changes permissions on files |
fcntl |
Controls operations on files and memory objects |
flock |
Locks a file as shared or exclusive |
fstat |
Provides information about file status |
ftruncate |
Sets the length of a memory object |
The
fchmod
function can be used to change access
permissions on a file.
If you are the owner of the file or have
superuser
privileges, you can use the
fchmod
function to set the access mode and grant or deny
permissions to the group, user, or others.
The
fcntl
function can be used to retrieve and set the value of the close-on-exec
flag, status flags and access modes, or set and clear locks.
Using the
fcntl
function, you can override locks set with the
flock
function.
The
fstat
function
returns information about the file, such as access permissions, link
references, and type and size of file.
You can use this function to obtain
information for use in subsequent calls to other file control functions.
You can apply a lock to a shared-memory object or mapped file by using a
variety of file control functions, including
fcntl
and
flock
.
Both these functions apply a lock on an open
file, but they differ in how the lock is performed and the range of other
tasks they can perform.
Note that the locks applied with these functions are for files, not file
descriptors.
That means that under most circumstances, file locks are not
inherited across a fork.
If a parent process holds a lock on a file and the
parent process forks, the child process will inherit the file descriptor,
but not the lock on the file.
A file descriptor that is duplicated with one
of the
dup
functions does not inherit the lock.
The
fcntl
function is used for general file control.
In
addition to locking and unlocking an open file, the
fcntl
function is used to return or set status, return a new file descriptor, or
return process IDs.
The
flock
function is limited to applying locks on a file
and is not used for general file control.
Refer to the online reference pages for more information on using file control functions.
Several functions let you manipulate and control access to memory-mapped
files and shared memory.
These functions include
msync
and
mprotect
.
Using these functions, you can modify
access protections and synchronize writing to a mapped file.
The
msync
function synchronizes the caching operations of
a memory-mapped file or shared-memory region.
Using this function, you can
ensure that modified pages in the mapped region are transferred to the
file's underlying storage device or you can control the visibility of
modifications with respect to file system operations.
Flags used on the
msync
function specify whether the
cache flush is to be synchronous (MS_SYNC), asynchronous (MS_ASYNC), or
invalidated (MS_INVALIDATE).
Either the MS_SYNC or MS_ASYNC flag can be
specified, but not both.
When you use the MS_SYNC flag, the
msync
function does
not return until all write operations are complete and the integrity of the
data is assured.
All previous modifications to the mapped region are visible
to processes using the
read
parameter.
When you use the MS_ASYNC flag, the
msync
function
returns immediately after all of the write operations are scheduled.
When you invalidate previously cached copies of the pages, other users are
required to get new copies of the pages from the file system the next time
they are referenced.
In this way, previous modifications to the file made
with the
write
function are visible to the mapped region.
When using the
msync
function, you should use pages
within the same address and length specified in the call to the
mmap
function to ensure that the entire mapped region is
synchronized.
The
mprotect
function changes the access protection of a
mapped file or shared-memory region.
When using the
mprotect
function, use pages within the same address and
length specified in the call to the
mmap
function.
Protection flags used on the
mprotect
function are the
same as those used on the
mmap
function.
Note that use of the
mprotect
function modifies access
only to the specified region.
If the access protection of some pages within
the range were changed by some other means, the call to the
mprotect
function may fail.
When a process has finished using a shared-memory segment, you can remove
the name from the file system namespace with a call to the
shm_unlink
function, as shown in the following example:
status = shm_unlink("my_file");
The
shm_unlink
function unlinks the shared-memory object.
Memory objects are persistent, which means the contents remain until all
references have been unmapped and the shared-memory object has been unlinked
with a call to the
shm_unlink
function.
Every process using the shared memory should perform the cleanup tasks of unmapping and closing.
You can lock and unlock a shared-memory segment into physical memory to
eliminate paging.
The MCL_FUTURE argument to the
mlockall
function causes new shared-memory regions to be locked automatically.
See
Chapter 4
for more information on using the
mlock
and
mlockall
functions.
Example 3-2 shows how to map a file into the address space of the process and lock it into memory. When the file is unmapped, the lock on the address is removed.
/* This program locks the virtual memory address that */ /* was returned from the mmap() function into memory. */ #include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <sys/file.h> #include <sys/mman.h> #include <sys/stat.h> #include <errno.h> main() { int fd; caddr_t pg_addr; int size = 5000; int mode = S_IRWXO|S_IRWXG|S_IRWXU; /* Create a file */ fd = shm_open("example", O_RDWR|O_CREAT, mode); if(fd < 0){ perror("open error "); exit(); } /* Set the size */ if((ftruncate(fd, size)) == -1){ perror("ftruncate failure"); exit(); } /* Map the file into the address space of the process */ pg_addr = (caddr_t) mmap(0, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, 0); if(pg_addr == (caddr_t) -1){ perror("mmap failure"); exit(); } /* Lock the mapped region into memory */ if(mlock(pg_addr,size) != 0){ perror("mlock failure"); exit(); } /* Unmap of the address region removes the memory lock */ /* established on the address region by this process */ if(munmap(pg_addr, size) < 0) perror("unmap error"); close(fd); shm_unlink("example"); exit(); }
You can also lock the file so that other processes cannot use it, making it an exclusive resource for a process and its descendants. See Section 3.1.4 for more information on locking files.
When using shared memory, processes map the same area of memory into their address space. This allows for fast interprocess communication because the data is immediately available to any other process using the same shared memory. If your application has multiple processes contending for the same shared-memory resource, you must coordinate access.
Semaphores provide an easy means of regulating access to a memory object and determining if the memory resource is available. Typically, an application will begin execution at a nonrealtime priority level, then perform the following tasks when using mapped or shared-memory objects and semaphores:
Create the shared-memory object
Determine the address and map the region into memory
Create a semaphore
Adjust the process priority and scheduling policy as needed
Before a read or write operation, lock (reserve) the semaphore
After a read or write operation, unlock (release) the semaphore
A process can lock the semaphore associated with a mapped or shared-memory object to indicate that the process requires exclusive access. Cooperating processes normally wait until the semaphore is unlocked before accessing a region.
Refer to Chapter 9 for information on semaphores and for an example using semaphores and shared memory.