The idea is to use a ring buffer. "A circular buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. This structure lends itself easily to buffering data streams."
[1]. The communication between two entities can by unidirectional or bidirectional. A ring buffer is considered a channel which is governed by a standard that dictates that each channel only support communication in one one particular direction. Each thread is a individual entity, and each process excluding the threads it contains is a individual entity which can be control a particular thread. To help protect this communication mechanism from malicious executable code there are some rules. These rules include not allowing duplicate directional channel instancing or hosting between two identical entities, providing entity channels instead of a global/one/single channel, and checking of the length field in the channel header along with the length in each message header. Priority of messages is provided on a entity level, but not message type which would be implemented outside the realm of this explanation.
The general implementation for a ring buffer header is as:
Code: Select all
// A ring buffer header implementation in C.
struct trbhdr
{
uint32_t length;
uint32_t used;
uint32_t write;
uint32_t read;
};
// A ring buffer message header implementation in C.
struct trbmhdr
{
uint32_t size;
};
The
length field designates the length of the buffer, and once you go past this length you wrap around back to offset zero in the buffer and the process will repeat indefinitely. The
used field designates the amount in bytes of all the remaining messages. The
write field holds a byte index inside the buffer in which a new message may be written with out overwriting pending data in the buffer (written previously, but not read or removed from the buffer). The
read field holds a byte index inside the buffer in which the next pending message is awaiting. The
write byte index must never exceed the
read byte index or you risk overwriting messages which are currently being read.
This structure stands against the abuse of concurrent access by only allowing the sending side to modify the
write field. While, the receiving side only modified the
read field. This produces a natural lock free scenario in which:
A reads
write, and writes
read and
B reads
read, and write
write. Also
A subtracts the length of each read messages from
used, while
B adds the length of each written message from
used which prevents the empty and full problem when using ring buffers.
The ring buffer is created as a region of memory shared between two memory contexts or the same memory context in special circumstances (but not normal ones) so that each thread has direct memory access to modifying the buffer with out invoking the kernel.
A entity shall be an individual thread of a process, or the process it's self. A thread may act as an entity while it also acts as the pseudo entity for the process. Multiple threads acting as an pseudo entity for the process must perform their own negotiating, mutual exclusion, locking, or any and all of the said when modifying rings instanced or hosting by the process as a individual entity.
Each ring buffer shall be considered a channel which is unidirectional (messages only go one direction). Bi-directional communication is performed by using two channels (one for each direction).
A entity shall instance or host no duplicate channels. A duplicate channel is when two or more channels exist between the same two entities in the same direction. This is to prevent a denial of service attack by malicious code creating thousands of identical channels.
A entity shall ensure that the channel header (and the each of the message header) fields are correct and not invalid before and during a operation of reading or writing for a channel.
A denial of service attack is still possible if malicious code in a process spawns a number of threads which allows it to circumvent the duplicate channel rule and create a arbitrary number of duplicate channels which depletes system resources. However to note that even in a modern Unix or Linux system this same problem can arise. This problem should be handled by user limitations instead of this explanation, and potentially the limitation for duplicates channels could be waived away as a preventative for malicious executable code - but before doing so one must realize the design methodology effect of doing so which are not implicitly stated above.