A memory object is a container for physical memory.
We need to consider the following aspects:
- Layout
- The physical composition of the object: the physical memory pages that actually back the mapped memory.
The pages might not yet exist; for example, if you've mapped a file, some of the pages might be in memory,
and some might not have been loaded from the file.
If you've allocated a contiguous object using
shm_ctl(),
the physical memory pages are in place.
Nevertheless, all the pages form the layout of the object, and
you can have more than one range of memory in an object.
- Content
- The data held by the object.
A memory object has a specific life cycle; the object has a creator (or owner)
and a reference count that the kernel maintains.
The reference count increases as the object is created, duplicated, connected to, or mapped.
It decreases when connections are severed, the object is unmapped, and so on.
When the reference count goes to zero, the kernel destroys the object.
Here are some examples:
- memory-mapped files
- shared memory objects that you open with
shm_open()
and then expand with
ftruncate()
- contiguous memory allocated with mmap(MAP ANON | MAP PHYS).
You don't see this object, and there's no way to change its layout, but the object is still there.
- per-process anonymous memory
Note:
The direct mapping of physical addresses doesn't result in a memory object.
The main APIs for working with memory objects include the following:
- shm_open()
- Create a new shared memory object or open an existing one, based on a path under
/dev/shmem.
- posix_typed_mem_open()
- Open a named memory pool that you can then allocate from.
- shm_ctl()
- A QNX Neutrino function that gives you fine control over the layout of an object
that was opened with shm_open().
- mmap()
- Expose memory in the virtual address space.
- ftruncate()
- Expand or contract an object that was opened with shm_open().
This gives you pageable anonymous memory.