mmap(), mmap64(), mmap_handle()

Updated: October 28, 2024

Map a memory region into a process's address space

Synopsis:

#include <sys/mman.h>

void * mmap( void * addr,
             size_t len,
             int prot,
             int flags,
             int filedes,
             off_t off );

void * mmap64( void * addr,
               size_t len,
               int prot,
               int flags,
               int filedes,
               off64_t off );

void * mmap_handle( void * addr,
                    size_t len,
                    int prot,
                    int flags,
                    shm_handle_t handle,
                    off_t off );

Arguments:

addr
NULL, or a pointer to where you want the object to be mapped into the calling process's address space.

This address doesn't have to be page-aligned (i.e., a multiple of PAGESIZE) but if you specify MAP_FIXED in flags, the address must be aligned with off, or the function fails. For more information, refer to the MAP_FIXED description below.

len
The number of bytes to map into the caller's address space. It can't be 0, but this value doesn't have to be page-aligned; that is, it doesn't have to be a multiple of PAGESIZE.
prot
The access capabilities that you want to use for the memory region being mapped. You can combine at least the following protection bits, as defined in <sys/mman.h>:
  • PROT_EXEC — the region can be executed.
    Note: To successfully use this flag, your process must have the PROCMGR_AID_PROT_EXEC ability enabled. For more information, see procmgr_ability().
  • PROT_NOCACHE — disable caching of the region (e.g., so it can be used to access dual-ported memory).
    Note: On 32- and 64-bit ARM targets, PROT_NOCACHE causes RAM to be mapped as normal noncached, but non-RAM to be mapped as strongly ordered device memory. For finer control, see shm_ctl_special().
  • PROT_NONE — the region can't be accessed.
  • PROT_READ — the region can be read.
  • PROT_WRITE — the region can be written (on both x86 and ARM architectures, PROT_WRITE also implies PROT_READ).
    Note: In order to simultaneously set PROT_EXEC and PROT_WRITE, your process must have the PROCMGR_AID_PROT_WRITE_AND_EXEC ability enabled (in addition to PROCMGR_AID_PROT_EXEC). For more information, see procmgr_ability().
flags
Flags that specify further information about handling the mapped region. POSIX defines the following:
  • MAP_PRIVATE
  • MAP_SHARED
  • MAP_FIXED

The following are Unix or QNX Neutrino extensions:

  • MAP_ANON
  • MAP_BELOW
  • MAP_ELF
  • MAP_FILE — defined for compatibility; has no effect.
  • MAP_LAZY
  • MAP_NOINHERIT
  • MAP_NORESERVE — defined for compatibility; has no effect.
  • MAP_NOSYNCFILE
  • MAP_PHYS
  • MAP_RENAME — defined for compatibility; has no effect.
  • MAP_STACK

For more information, see below.

filedes
(mmap() and mmap64() only) The file descriptor for a file, shared memory object, or typed memory object. If you're mapping anonymous or physical memory, this argument must be NOFD.

Memory that's mapped from a typed-memory file descriptor is implicitly locked. Shared memory objects that are populated with shm_ctl() are implicitly locked, unless you use the SHMCTL_LAZY flag.

handle
(mmap_handle() only) A shared memory object handle created by shm_create_handle().
off
The offset into the file or memory object of the region that you want to start mapping, or a physical address (e.g., for mapping a device's registers in a resource manager).

Both the addr and off values get page-aligned by the function, so the returned pointer refers to the start of the corresponding memory page plus this offset (i.e., the exact physical address of the memory block). For instance, if off is 8, then the mapped address that gets returned is modulo PAGESIZE 8.

If you specify MAP_FIXED in flags, off must be aligned with addr, or the function fails. For more information, refer to the MAP_FIXED description below.

Note: On x86, if a device's registers are memory-mapped, you can use mmap_device_memory() to access them. Otherwise you can use mmap_device_io() to gain access to the device, and routines such as in8() and out8() to access them. On architectures other than x86, both methods are equivalent.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The mmap() and mmap64() functions map a region within the object specified by filedes, beginning at off and continuing for len, into the caller's address space and return the location.

Note: The mmap64() function is a large-file support version of mmap(). The large-file support functions and data types appear in the namespace only if you define _LARGEFILE64_SOURCE when you compile your code. For more information, see Classification in “What's in a Function Description?”.
CAUTION:
To avoid undesired behavior, don't use mmap() to map the same region of typed memory cached, unmap() it, then mmap() it uncached. You need to ensure that the cache is flushed between calls to mmap().

The mmap_handle() function is similar to mmap(), but it uses the shared memory object handle created by shm_create_handle(), instead of a file descriptor.

The object that you map from can be one of the following:

If filedes isn't NOFD, you must have opened the file descriptor for reading, no matter what value you specify for prot; write access is also required for PROT_WRITE if you haven't specified MAP_PRIVATE.

The mapping is as shown below:


Figure 1. Mapping memory with mmap().

Typically, you don't need to use addr; you can just pass NULL instead. Mappings, including the flags, are maintained across a fork().

The flags argument includes a type (masked by the MAP_TYPE bits) and additional bits. You must specify one of the following types:

MAP_PRIVATE
The mapping is private to the calling process; changes aren't propagated back to the underlying object (if any). For this type of mapping, mmap() allocates system RAM and copies the current object.
MAP_SHARED
The mapping may be shared by many processes; changes are propagated back to the underlying object.
CAUTION:
  • Don't have a shared, writable mapping to a file that you're simultaneously accessing via write(). The interaction between the two methods isn't well defined and may give unexpected results.
  • If you don't specify either MAP_PRIVATE or MAP_SHARED, mmap() returns MAP_FAILED and sets errno to EINVAL.

You can OR the following flags into the above type to further specify the mapping:

MAP_ANON
Map anonymous memory that isn't associated with a specific file descriptor; the filedes parameter must be NOFD. The mmap() function allocates the memory and fills it with zeros. This is equivalent to opening /dev/zero.

MAP_ANON is most commonly used with MAP_PRIVATE, but you can use it with MAP_SHARED to create a shared memory area for forked applications:

  • MAP_ANON | MAP_PRIVATE — a new mapping is made for the child process, and contents of the object are initially the same as for the parent. Each process works with its own object, and changes made by one process aren't seen by the other.
  • MAP_ANON | MAP_SHARED — the parent and child processes share the same object. Changes made by one process are seen by the other.
Note: If the mapping of MAP_ANON is done with PROT_NONE, then the appropriate virtual address space will be reserved in the process. No system RAM will be allocated for this mapping. A later call to mprotect() may trigger the needed allocation. For more information, see the description section of mprotect().
MAP_BELOW

Map below the given addr, if possible. The addr argument to mmap*() is a hint as to where to put the mapped region in the caller's address space (unless combined with MAP_FIXED, which turns it into a strong requirement). MAP_BELOW changes the meaning of the hint to be where the region should end instead of where it should begin.

If MAP_BELOW is set, the memory manager searches downwards, proceeding to the bottom of the address space, and may wrap around if no hole of the required size is available. Therefore, the mapping may be made at an address larger than addr.

If MAP_BELOW isn't set, the memory manager searches upwards, proceeding to the top, and may wrap around if necessary. Therefore, the mapping may be made at an address smaller than addr.

MAP_ELF
The memory is an ELF object. This flag is used by the program loader.
MAP_FILE
Map a regular file. This flag is required on some operating systems, but has no effect on QNX Neutrino.
MAP_FIXED
Map the object to the address specified by addr, or the function fails. If this area is already mapped, the call changes the existing mapping of the area. A memory area being mapped with MAP_FIXED is first unmapped by the system using the same memory area. See munmap() for details.
CAUTION:
  • In order to use MAP_FIXED, your process must have the PROCMGR_AID_MAP_FIXED ability enabled. For more information, see procmgr_ability().
  • Use MAP_FIXED with caution because it removes any existing mappings, making it easy for a process to corrupt its own address space. In general, you should assume that you can use MAP_FIXED only at an address (and size) that a call to mmap*() without MAP_FIXED returned.
  • If you set MAP_FIXED, the addresses in the addr and the off arguments must be aligned with respect to page boundaries; that is, the two values must be equal modulo the page size, or the function fails. For example, the following call generates an error:
    paddr = mmap(vaddr+7, 1, PROT_READ, MAP_SHARED|MAP_FIXED, fd, 1);
    
    Here, we assume that vaddr contains a page-aligned address and we deliberately add 7 bytes to attain an address that's a bit beyond the start of the memory page, and pass this into the addr argument. Because we set the off argument to 1, these two values are misaligned. If we specified vaddr + 1, then the call would succeed.

If addr isn't NULL and you don't set MAP_FIXED, then the value of addr is taken as a hint as to where to map the object into the calling process's address space. The mapped area won't overlay any current mapped areas. Refer to the MAP_BELOW description above for information on what happens if the hint in addr can't be followed.

MAP_LAZY
Delay acquiring system memory, and copying or zero-filling the MAP_PRIVATE or MAP_ANON pages, until an access to the area has occurred. If you set this flag, and there's no system memory at the time of the access, the thread gets a SIGBUS with a code of BUS_OBJERR. This flag is a hint to the memory manager.
MAP_NOINHERIT
If the process calls fork(), don't give the child process a copy of the memory mapping.
MAP_NORESERVE
Defined for compatibility, but has no effect.
MAP_NOSYNCFILE
Don't update the underlying file.
CAUTION:
If the same region in a file is mapped twice, once with MAP_NOSYNCFILE and once without, the memory manager might not be able to tell whether a change was made through the MAP_NOSYNCFILE mapping or not, and thus write out changes that weren't intended.
MAP_PHYS
Physical memory is required. The filedes parameter must be NOFD. When you use this flag without MAP_ANON, the offset specifies the exact physical address to map (e.g., for video frame buffers).
Warning:

Direct physical mappings are extremely dangerous, especially when the mapped region overlaps RAM and is not used for mapping device memory. The memory manager does not track such mappings as allocations and, thus, the physical memory may be freed and then potentially reallocated while the mappings exist. The implications both to safety and security are severe.

QNX Neutrino provides better mechanisms for scenarios traditionally handled by such mappings, including typed memory and shared-memory handles.

If you do have to use MAP_PHYS to map physical memory, your process must have the PROCMGR_AID_MEM_PHYS ability enabled; you don't need this ability if you also specify MAP_ANON. For more information, see procmgr_ability().

If you use MAP_PHYS with MAP_ANON, mmap*() allocates physically contiguous memory and ignores the offset. You should almost always use these flags with MAP_SHARED; if you use them with MAP_PRIVATE, QNX Neutrino creates a physically contiguous memory object with zero-filled pages and then privatizes it, meaning the OS creates a copy in which there's no guarantee of contiguity.

If you're mapping device memory or registers, you should use MAP_PHYS with MAP_SHARED. If you use MAP_PHYS with MAP_PRIVATE but without MAP_ANON to map device memory or registers, then everything works until you write to any of the pages. At this point, the pages are copied and what you're pointing to is memory that isn't associated with the device you're trying to control, and the device will seem not to respond.

Warning: You may run into trouble if you specifiy MAP_LAZY with MAP_PHYS because this combination requests lazy allocation of physically contiguous memory.

The memory manager supports a backward-compatibility mode that you can control with the b and ~b settings for procnto memory configuration options. If backward-compatibility is enabled (the default) and you specify MAP_PHYS with MAP_PRIVATE, then the memory manager changes MAP_PRIVATE to MAP_SHARED. If backward-compatibility is disabled and you specify MAP_PHYS with MAP_PRIVATE, then mmap*() creates a private mapping of the physical object, which means the contents of that memory are copied into newly-allocated pages.

MAP_RENAME
Defined for compatibility, but has no effect.
MAP_STACK
This flag tells the memory allocator what the MAP_ANON memory will be used for. It's only a hint.

The following flag is defined in <sys/mman.h>, but you shouldn't use it:

MAP_SYSRAM
An output-only status flag for the DCMD_PROC_MAPINFO and DCMD_PROC_PAGEDATA devctl() commands. For more information, see Controlling processes via the /proc filesystem in the “Processes” chapter of the QNX Neutrino Programmer's Guide.
Note: Specifying the MAP_SYSRAM bit in a call to mmap*() will result in an error of EINVAL.

If filedes represents a typed memory object opened with either the POSIX_TYPED_MEM_ALLOCATE or POSIX_TYPED_MEM_ALLOCATE_CONTIG flag (see posix_typed_mem_open()), and there are enough resources available, mmap() maps len bytes allocated from the corresponding typed memory object that weren't previously allocated to any process in any processor that may access that typed memory object. If there aren't enough resources available, mmap() fails.

If filedes represents a typed memory object opened with the POSIX_TYPED_MEM_ALLOCATE_CONTIG flag, the allocated bytes are contiguous within the typed memory object. If the typed memory object was opened with POSIX_TYPED_MEM_ALLOCATE, the allocated bytes may be composed of noncontiguous fragments within the typed memory object. If the typed memory object was opened with neither of these flags, len bytes starting at the given offset within the typed memory object are mapped, exactly as when mapping a file or shared memory object. In this case, if two processes map an area of typed memory using the same offset and length and using file descriptors that refer to the same memory pool (either from the same port or from a different port), both processes map the same region of storage.

Returns:

The address of the mapped-in object, or MAP_FAILED if an error occurred (errno is set).

Errors:

EACCES
One of the following occurred:
  • The file descriptor in filedes isn't open for reading.
  • You specified PROT_WRITE and MAP_SHARED, and filedes isn't open for writing.
  • You specified PROT_EXEC for a memory-mapped file mapping, the file doesn't have execute permission for the client process, and procnto was started with the -mX option.
EAGAIN
The mapping couldn't be locked in memory, if required by mlockall(), because of a lack of resources.
EBADF
The file descriptor in filedes is invalid.
EBUSY
The resource you're trying to map is busy.
EINVAL
One of the following occurred:
  • The len argument is 0.
  • The prot argument is invalid.
  • You didn't specify MAP_PRIVATE or MAP_SHARED.
  • You included MAP_SYSRAM in the flags. This bit is an output-only status flag for the DCMD_PROC_MAPINFO and DCMD_PROC_PAGEDATA devctl() commands.
  • You specified MAP_PHYS with MAP_PRIVATE, and you aren't using the backward-compatibility memory-manager option (procnto -m~b). If you're using this option (procnto -mb), mmap() assumes you meant MAP_SHARED.
  • You specified MAP_ANON or MAP_PHYS, and filedes isn't NOFD.
  • You specified MAP_PHYS, and the object goes beyond the last physical address that the architecture supports.
  • The sum of the offset and the length of the required memory object wraps around past 0.
  • You specified MAP_PHYS, MAP_LAZY, and MAP_ANON.
EMFILE
The number of mapped regions has reached the maximum limit; see the RLIMIT_AS and RLIMIT_DATA resources for setrlimit().
ENODEV
The filedes argument refers to an object for which mmap() is meaningless (e.g., a terminal).
ENOMEM
One of the following occurred:
  • You specified MAP_FIXED, and the address range requested is outside of the allowed process address range, or there wasn't enough memory to satisfy the request.
  • The mapping couldn't be locked in memory, if required by mlockall(), because there wasn't enough memory.
  • There aren't enough unallocated resources remaining in the typed memory object associated with the file descriptor.
ENXIO
One of the following occurred:
  • Addresses in the range [off, off+len) are invalid for the object specified by filedes.
  • You specified MAP_FIXED, and addr, len, and off are invalid for the requested object.
  • The file descriptor refers to a typed memory object that the calling process can't access.
EOVERFLOW
The file is a regular file, and the offset plus the length exceeds the maximum offset.
EPERM
The calling process doesn't have the required permission (see procmgr_ability()), or it attempted to set PROT_EXEC for a region of memory covered by an untrusted memory-mapped file.
ESRCH
(mmap_handle() only) The shared memory object handle was invalid.

Examples:

Open a shared memory object and share it with other processes:

fd = shm_open( "/datapoints", O_RDWR, 0777 );
addr = mmap( 0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );

Allocate a physically contiguous DMA buffer for a bus-mastering PCI network card:

addr = mmap( 0,
             262144,
             PROT_READ | PROT_WRITE | PROT_NOCACHE,
             MAP_SHARED | MAP_PHYS | MAP_ANON,
             NOFD,
             0 );

Map a file into memory, change the memory, and then verify that the file's contents have been updated:

#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define TESTSTRING "AAAAAAAAAA"

int main(int argc, char *argv[]) {
    char buffer[80], filename[200] = "/tmp/try_it";
    int fd, file_size, ret, size_written, size_read;
    void *addr;
    
    /* Write the test string into the file. */
    unlink( filename);
    fd = open( filename, O_CREAT|O_RDWR , 0777 );
    if( fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
    size_written = write( fd, TESTSTRING, sizeof (TESTSTRING) );
    if ( size_written == -1 ){
        perror("write");
        exit(0);
    }   
    printf( "Wrote %d bytes into file %s\n", size_written, filename );
    
    lseek( fd, 0L, SEEK_SET );
    file_size = lseek( fd, 0L, SEEK_END );
    printf( "Size of file = %d bytes\n", file_size );

    /* Map the file into memory. */
    addr = mmap( 0, file_size, PROT_READ | PROT_WRITE, MAP_SHARED,
                 fd, 0 );
    if (addr == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    } 

    /* Change the memory and synchronize it with the disk. */
    memset( addr, 'B', 5 );
    ret = msync( addr, file_size, MS_SYNC);
    if( ret == -1) {
        perror("msync");
        exit(0);
    }
    
    /* Close and reopen the file, and then read its contents. */
    close(fd);
    fd = open( filename, O_RDONLY );
    if( fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
    size_read = read( fd, buffer, sizeof( buffer ) );
    printf( "File content = %s\n", buffer );
    
    close(fd);
    return EXIT_SUCCESS;
}

Classification:

mmap() is POSIX 1003.1 SHM|TYM; mmap64() is Large-file support; mmap_handle() is QNX Neutrino

Safety:  
Cancellation point No
Interrupt handler No
Signal handler Yes
Thread Yes