shm_ctl()

Give special attributes to a shared memory object

Synopsis:

#include <sys/mman.h>

int shm_ctl( int fd,
             int flags,
             uint64_t paddr,
             uint64_t size );

Arguments:

fd
The file descriptor that's associated with the shared memory object, as returned by shm_open().
flags
One or more of the following bits, defined in <sys/mman.h>:

Note: Some of the bits have specific meanings for an ARM processor. For more information, see Flags for ARM processors,” below.

paddr
A physical address to assign to the object, if you set SHMCTL_PHYS in flags.
size
The new size of the object, in bytes, regardless of ANON/PHYS flag.

Library:

libc

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

Description:

The shm_ctl() function modifies the attributes of the shared memory object identified by the handle, fd. This handle is the value returned by shm_open().


Note:
  • The combination SHMCTL_ANON | SHMCTL_PHYS has the same behavior as for mmap(): it indicates that you want physically contiguous RAM to be allocated for the object.
  • On ARM targets, once you've called shm_ctl() for a shared memory object, you can't resize it. You must unmap and unlink the shared object, and then recreate it.
  • If you specify SHMCTL_PHYS in the flags, then paddr and size must be even multiples of the page size (sysconf(_SC_PAGE_SIZE)).

The shm_ctl_special() function is similar to shm_ctl(), but has an additional, processor-specific argument.

Flags for ARM processors

For an ARM processor, the behavior for different combinations of flags arguments is as follows:

SHMCTL_ANON
Create a shared memory object using anonymous memory (i.e. zero-filled, dynamically allocated RAM). This memory isn't guaranteed to be physically contiguous.

When you mmap(), it's mapped into the normal process address space, so the mapping is subject to the 32M address space limit on ARM.

SHMCTL_ANON | SHMCTL_PHYS
Same as SHMCTL_ANON, except that the memory allocated is physically contiguous.

When you mmap(), it's mapped into the normal process address space, so the mapping is subject to the 32M address space limit on ARM.

SHMCTL_ANON | SHMCTL_GLOBAL
Allocate memory as for SHMCTL_ANON.

When you mmap(), it's mapped at a globally visible address outside the regular process address space. This allows the object to be up to 1GB in size.

If more than one process maps the object, it appears at the same virtual address in all processes that map it. This virtual address is assigned at the time the first mmap() of the object is performed.

The mmap() call must map the whole object (i.e. offset must be 0, and len must be the size of the object set by shm_ctl()).

The mappings are protected such that only those processes that have mapped the object can access it. Any other process that attempts to access the (globally visible) virtual address fails. This impacts context switches to and from the mapping process because MMU page tables must be modified to grant and remove the access permissions on each context switch.

The additional cost of this manipulation during context switches includes modifying one Level 1 table entry for each megabyte of the mapping, followed by a TLB flush.

SHMCTL_ANON | SHMCTL_GLOBAL | SHMCTL_PRIV
Same as SHMCTL_ANON | SHMCTL_GLOBAL, except that no per-process protection is performed. Instead, the access permissions for the (global) mapping are set, so that only processes that have I/O privileges (i.e. have called ThreadCtl() with a command of NTO_TCTL_IO) can access the mappings.

This avoids the performance impact during context switches, but allows potential access by any process that has I/O privileges. I/O privileges are normally required only for driver or other system processes, so this combination provides some protection from potential access by normal user processes.

SHMCTL_ANON | SHMCTL_GLOBAL | SHMCTL_LOWERPROT
Same as SHMCTL_ANON | SHMCTL_GLOBAL, except that no per-process protection is performed; any process can access the mappings.

This avoids the performance impact during context switches and also avoids requiring I/O privileges to access the mappings. However, there is no protection from potential access by other processes.

SHMCTL_PHYS
Create an object to map the physical address specified by the offset parameter to the shm_ctl() call.

When you mmap(), it's mapped at a globally visible address outside the normal process address space. This allows the object to be up to 1GB in size.

All mappings are forced to PROT_NOCACHE to avoid cache aliases (due to the ARM virtually indexed cache)

Each process that maps, the object gets a new mapping.

The mappings are protected such that only those processes that have mapped the object can access it. Any other process that attempts to access the (globally visible) virtual address fail. This impacts context switches to and from the mapping process because MMU page tables must be modified to grant and remove the access permissions on each context switch.

The additional cost of this manipulation during context switches includes modifying one Level 1 table entry for each megabyte of the mapping, followed by a TLB flush.

SHMCTL_PHYS | SHMCTL_PRIV
Same as SHMCTL_PHYS, except that mappings aren't protected. Instead, access is allowed only by processes with I/O privileges.

This avoids the performance impact during context switches.

SHMCTL_PHYS | SHMCTL_LOWERPROT
Same as SHMCTL_PHYS, except that mappings aren't protected. Access is allowed for any process.
SHMCTL_PHYS | SHMCTL_GLOBAL
Same as SHMCTL_PHYS, except that all processes that map the object use the same virtual address. This virtual address is assigned by the first mmap() call to map the object.

Because all processes use the same (globally visible) virtual address, these mappings aren't forced to PROT_NOCACHE. If you need uncached behavior, specify PROT_NOCACHE when you call mmap().

The mappings are protected such that only those processes that have mapped the object can access it. Any other process that attempts to access the (globally visible) virtual address will fault. This impacts context switches to and from the mapping process because MMU page tables must be modified to grant and remove the access permissions on each context switch.

The additional cost of this manipulation during context switches includes modifying one Level 1 table entry for each megabyte of the mapping, followed by a TLB flush.

SHMCTL_PHYS | SHMCTL_GLOBAL | SHMCTL_PRIV
Same as SHMCTL_PHYS | SHMCTL_GLOBAL, except that mappings aren't protected. Instead, access is allowed only by processes with I/O privileges.

This avoids the performance impact during context switches.

SHMCTL_PHYS | SHMCTL_GLOBAL | SHMCTL_LOWERPROT
Same as SHMCTL_PHYS | SHMCTL_GLOBAL, except that mappings aren't protected. Instead, access is allowed by any process.

This avoids the performance impact during context switches.

Returns:

0
Success.
-1
An error occurred (errno is set).

Errors:

EINVAL
An invalid combination of flags was specified, or the shared memory object is already “special.”

Examples:

The following examples go together. Run sharephyscreator, followed by sharephysuser.

The sharephyscreator process maps in an area of physical memory and then overlays it with a shared memory object. The sharephysuser process opens that shared memory object in order to access the physical memory.

/*
 *  sharephyscreator.c
 *
 *  This maps in an area of physical memory and then
 *  overlays it with a shared memory object.  This way, another process
 *  can open that shared memory object in order to access the physical
 *  memory.  The other process in this case is sharephysuser.
 *
 *  Note that the size and address that you pass to shm_ctl() must be
 *  even multiples of the page size (sysconf(_SC_PAGE_SIZE)).
 *
 *  For VGA color text mode video memory:
 *    sharephyscreator /wally b8000
 *  Note that for VGA color text mode video memory, each character
 *  is followed by an attribute byte.  Here we just use a space.
*/

#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/neutrino.h>
#include <sys/stat.h>

char        *progname = "sharephyscreator";

main( int argc, char *argv[] )
{
    char        *text = "H e l l o   w o r l d ! ";
    int         fd, memsize;
    char        *ptr, *name;
    uint64_t    physaddr;
    
    if ( argc != 3 ) {
        printf( "Use: sharephyscreator shared_memory_object_name \
physical_address_in_hex\n" );
        printf( "Example: sharephyscreator wally b8000\n" );
        exit( EXIT_FAILURE );
    }
    name = argv[1];
    physaddr = atoh(argv[2]);
    memsize = sysconf( _SC_PAGE_SIZE ); /* this should be enough
                                           for our string */
   
    /* map in the physical memory */
    
    ptr = mmap_device_memory( 0, memsize, PROT_READ|PROT_WRITE, 0, physaddr );
    if ( ptr == MAP_FAILED ) {
        printf( "%s: mmap_device_memory for physical address %llx failed: %s\n",
            progname, physaddr, strerror(errno) );
        exit( EXIT_FAILURE );
    }

    /* open the shared memory object, create it if it doesn't exist */
    
    fd = shm_open( name, O_RDWR | O_CREAT, 0 );
    if ( fd == -1 ) {
        printf( "%s: error creating the shared memory object '%s': %s\n",
                progname, name, strerror(errno) );
        exit( EXIT_FAILURE );
    }
    
    /* overlay the shared memory object onto the physical memory */
    
    if ( shm_ctl( fd, SHMCTL_PHYS, physaddr, memsize ) == -1 ) {
        printf( "%s: shm_ctl failed: %s\n", progname, strerror(errno) );
        close( fd );
        munmap( ptr, memsize );
        shm_unlink( name );
        exit( EXIT_FAILURE );
    }
    strcpy( ptr, text ); /* write to the shared memory */
    
    printf( "\n%s: Physical memory mapped in, shared memory overlaid onto it.\n"
            "%s: Wrote '%s' to physical memory.\n"
            "%s: Sleeping for 20 seconds.  While this program is sleeping\n"
            "%s: run 'sharephysuser %s %d'.\n",
            progname, progname, ptr, progname, progname, name,
            strlen(text)+1 );
    sleep( 20 );
    
    printf( "%s: Woke up.  Cleaning up and exiting ...\n", progname );
    
    close( fd );
    munmap( ptr, memsize );
    shm_unlink( name );
}

The following is meant to be run with sharephyscreator.

/*
 *  sharephysuser.c
 *
 *  This one is meant to be run in tandem with sharephyscreator.
 *
 *  Run it as: sharephysuser shared_memory_object_name length
 *  Example: sharephysuser wally 49
 *
*/

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

char        *progname = "sharephysuser";

main( int argc, char *argv[] )
{
    int     fd, len, i;
    char    *ptr, *name;
    
    if ( argc != 3 ) {
        fprintf( stderr, "Use: sharephysuser shared_memory_object_name \
length\n" );
        fprintf( stderr, "Example: sharephysuser wally 49\n" );
        exit( EXIT_FAILURE );
    }
    name = argv[1];
    len = atoi( argv[2] );
    
    /* open the shared memory object */
    
    fd = shm_open( name, O_RDWR, 0 );
    if ( fd == -1 ) {
        fprintf( stderr, "%s: error opening the shared memory object '%s': %s\n",
                progname, name, strerror(errno) );
        exit( EXIT_FAILURE );
    }

    /* get a pointer to a piece of the shared memory, note that we
       only map in the amount we need to */
    
    ptr = mmap( 0, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
    if ( ptr == MAP_FAILED ) {
        fprintf( stderr, "%s: mmap failed: %s\n", progname, strerror(errno) );
        exit( EXIT_FAILURE );
    }

    printf( "%s: reading the text: ", progname );
    for ( i = 0; i < len; i++ )
        printf( "%c", ptr[i] );
    printf( "\n" );

    close( fd );
    munmap( ptr, len );
}

Classification:

QNX Neutrino

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

See also:

mmap(), munmap(), mprotect(), shm_ctl_special(), shm_open(), shm_unlink(), ThreadCtl()