Give special attributes to a shared memory object
#include <sys/mman.h> int shm_ctl( int fd, int flags, _uint64 paddr, _uint64 size );
If you create anonymous shared memory objects by calling mmap() with MAP_ANON | MAP_SHARED and a file descriptor of -1), a MAP_LAZY flag implicitly sets the SHMCTL_LAZY flag on the object.
Some of the bits have specific meanings for an ARM processor. For more information, see “Flags for ARM processors,” below. |
libc
Use the -l c option to qcc to link against this library. This library is usually included automatically.
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().
|
The shm_ctl_special() function is similar to shm_ctl(), but has an additional, processor-specific argument.
For an ARM processor, the behavior for different combinations of flags arguments is as follows:
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.
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.
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.
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.
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.
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.
This avoids the performance impact during context switches.
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.
This avoids the performance impact during context switches.
This avoids the performance impact during context switches.
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 overlayed 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 ); }
Safety: | |
---|---|
Cancellation point | Yes |
Interrupt handler | No |
Signal handler | Yes |
Thread | Yes |
mmap(), munmap(), mprotect(), shm_ctl_special(), shm_open(), shm_unlink(), ThreadCtl()