Previous Index Next

Memory Protocol

The memory protocol provides generic interface to continuous unstructured byte arrays (blocks of operating memory, files, disk drives, etc). Entities which are represented as byte arrays are called memory segments. Memory segments always start at byte address 0, and may have variable or fixed size. To allow concurrent accesses to parts of memory segments the memory protocol operates on memory objects which represent parts of memory segments of some size and starting at some byte address (offset).

Memory objects associated with the same segment may overlap, or be partially or completely outside of the segment (in this case the actual size of a memory object is smaller than its logical size). Memory object with upper boundary extending up to some very large address is the common representation for "file pointers".

Segment sizes, memory object offsets and sizes are 63-bit unsigned integer numbers, so the upper limit of memory segment size is 8 exabytes. Several memory protocol requests read or write binary user structures; so inter-platform message format is used to encode those structures.

Memory protocol is a superset of the stream protocol, and supports all stream protocol's requests. This means that memory objects may be treated as byte streams for sequential I/O.

Memory protocol includes the following requests:

ST_READ([mem{MEM_WRITE}, ...])

This request copies data from the requested memory object into argument memory objects and then shifts the lower boundary of the requested memory object upwards by the number of bytes copied (upper boundary is not changed). The action of ST_READ is atomic, i.e. concurrent requests using the requested object's size or offset values are excluded.

The number of bytes to copy is determined as minimum between the requested memory object's actual size and sum of in-arguments' actual sizes.

The result code of this request is the actual number of bytes copied. Since result codes are positive values of 32-bit signed integers, the maximal number of bytes which can be read by a single ST_READ request is 231-1.

For compatibility with stream protocol version of ST_READ using this request with no in-arguments causes the lower boundary of the requested memory object to be moved up to the upper boundary or segment's upper boundary, whichever address is smaller. This has the effect of reducing the actual size of the requested memory object to zero. The result code in this case is the number of bytes skipped.

Cancellation of ST_READ will cause it to return as soon as possible, with the actual number of data bytes copied as the result code.

ST_WRITE([mem{MEM_READ}, ...])

This request copies data into the requested memory object from argument memory objects and then shifts the lower boundary of the requested memory object upwards by the number of bytes copied (upper boundary is not changed). If the new memory object's lower boundary is beyond the segment's upper boundary then size of the segment will be increased accordingly (if possible). The action of ST_WRITE is atomic, i.e. concurrent requests using the requested object's size or offset values are excluded.

The number of bytes to copy is determined as minimum between the requested memory object's size (or actual size, if segment resizing is not allowed) and sum of in-arguments' actual sizes.

The result code of this request is the actual number of bytes copied. Since result codes are positive values of 32-bit signed integers, the maximal number of bytes which can be read by a single ST_WRITE request is 231-1.

If a null object is specified as one of the arguments all previous modifications of the memory segment's data will be commited to non-volatile memory (i.e. non-volatile memory will be synchronized with the state of the cached data). This does not cause ST_WRITE to wait for the completion of the synchronization. If memory segment's data is not cached using null object will have no effect.

As a special case, ST_WRITE without in-arguments can be used to wait for all modified data to be stored in non-volatile memory (synchronization is implicitly initiated before waiting in this case, so doing ST_WRITE with null object prior to ST_WRITE without arguments is redundant).

Cancellation of ST_WRITE will cause it to return as soon as possible, with the actual number of data bytes written as the result code.

MEM_LOCK(locker_id) -> lock{*}

MEM_LOCK is synonymous to ST_LOCK (i.e. it uses the same request code), however its semantics is different.

MEM_LOCK creates a new lock object (lock) associated with the requested memory object. This object can be used to implement advisory locks thus allowing several processes to update and use the memory segment simultaneously. locker_id is an arbitrary object identifying the owner of the lock (see definition of LOCK_CHECK).

Permissions associated with the access to the new lock object depend on permissions of the access to the requested memory object. If reading is allowed (ST_READ or MEM_READ is permitted), the resulting access to the lock object will allow shared locking (i.e. it will allow LOCK_SHARED and LOCK_NB_SHARED requests). If writing is allowed (ST_WRITE or MEM_WRITE is permitted), the resulting access to the lock object will allow exclusive locking (i.e. it will allow LOCK_EXCLUSIVE and LOCK_NB_EXCLUSIVE requests).

The locks on objects associated with the same memory segment are interdependent if memory objects overlap, i.e. locking some part of memory segment will cause locking of all memory objects overlapping this part of memory segment. However, the position of boundaries of memory object at the time of closing the lock is used for locking purposes; when locked memory object changes size interdependency of locks is not affected. This means that increasing size of a locked memory object is not safe and should be avoided.

MEM_INFO(mem{MEM_WRITE} mem_sizes)

MEM_INFO stores information about memory object into the message buffer specified by its only in-argument. The information is encoded using Grâl inter-platform message format, and user programs must decode it before use. Size of the buffer must be sufficient for the message.

The host-specific representation of the message is defined by the structure mem_sizes, which has the following fields:

m_pagesize
Size of memory segment's pages or blocks, in bytes. This information is purely advisory and can be used to optimize I/O requests.
m_offset
Offset of the memory object, in bytes.
m_size
Size of the memory object, in bytes. Note that the actual size of the memory object can be calculated as min(m_size, max(0, m_ssize-m_offset)).
m_ssize
Size of the entire memory segment, in bytes.

This request is granted.

MEM_DUP() -> mem

MEM_DUP creates a copy of the memory object (associated with the same segment, and with the same offset and size as original) and returns access to it, with the same permissions and lifetime as the original.

This request is granted.

MEM_READ([mem{MEM_WRITE}, ...])

MEM_READ is the same as ST_READ but does not change boundaries of the requested memory object.

MEM_WRITE([mem{MEM_READ}, ...])

MEM_WRITE is the same as ST_WRITE but does not change boundaries of the requested memory object. Size of the memory segment may be increased as in case with ST_WRITE (sum of the actual number of bytes copied plus lower boundary's address is used as the segment's new upper boundary).

MEM_REMAP(mem{MEM_READ} mem_resize)
MEM_RESIZE(mem{MEM_READ} mem_resize)

Those requests are used to change boundaries of the requested memory object and/or size of the associated memory segment. The resizing operation's code and its arguments are retrieved from the message buffer specified by the request's only in-argument. This message should be encoded using Grâl inter-platform message format.

The host-specific representation of the message is defined by the structure mem_resize, which has the following fields:

m_op
Memory operation, encoded as a bit mask with three 2-bit fields:

see description of the memory operation bits below.
m_offset
Signed offset modificator.
m_size
Signed size modificator.

MEM_REMAP only allows to change object's size and offset so the new memory object's boundaries will be within the boundaries the object had when it was first created (by MEM_DUP or other means); it also does not allow memory segment to be truncated. In other words, MEM_REMAP is the "safe" operation which does not allow access to the data beyond the boundaries of the original memory object. Conversely, MEM_RESIZE allows to change memory object's and memory segment's boundaries arbitrarily.

The following tables detail the memory operation encoding:

Operations on offset of the memory object
Bits
(hex)
Operation
Name
Description
0x0 MO_REL_OFF Sets offset relatively to the old one.
new_offset = old_offset + m_offset
0x1 MO_ABS_OFF Sets absolute offset value.
new_offset = m_offset
0x2 MO_END_OFF Sets offset relatively to the end of segment.
new_offset = old_segsize + m_offset
0x3 - Illegal memory operation.

Operations on size of the memory object
Bits
(hex)
Operation
Name
Description
0x0 MO_REL_SIZE Sets size relatively to the old one.
new_size = old_size + m_size
0x4 MO_ABS_SIZE Sets size as absolute value.
new_size = m_size
0x8 MO_END_SIZE Sets upper boundary relatively to the end of
the segment.
new_size = old_segsize - new_offset + m_size
0xC MO_ADJ_SIZE Sets upper boundary relatively to its old
position.
new_size = old_offset - new_offset + m_size

Operations on size of the memory segment
Bits
(hex)
Operation
Name
Description
0x00 - Leaves segment size unchanged.
0x10 MO_EXPAND Expands segment by the new upper boundary
of the memory object.
new_segsize = max(old_segsize,
new_offset + new_size)

0x20 MO_TRUNCATE Sets segment size by the new upper boudary
of the memory object (MEM_RESIZE only).
new_segsize = new_offset + new_size
0x30 - Illegal memory operation.

MEM_REMAP is granted.

MEM_FREEPAGES(mem{MEM_WRITE} mem_freepages)

MEM_FREEPAGES stores information about memory object's medium utilization into the message buffer specified by its only in-argument. The information is encoded using Grâl inter-platform message format, and user programs must decode it before use. Size of the buffer must be sufficient for the message.

The host-specific representation of the message is defined by the structure mem_freepages, which has the following fields:

m_pagesize
Size of medium's pages or blocks, in bytes. Note that this size may be different from memory object's page size returned by MEM_INFO if the memory segment's data is cached in a medium with different page size.
m_free
The number of free blocks or pages on the medium.
m_total
Total number of blocks or pages on the medium.


Previous Index Next