Message passing is the fundamental functionality of the operating system kernel which acts as a basic method of interaction between operating system components. Message passing in Phoenix-RTOS is synchronous. Sending thread is suspended until the receiving thread receives a message and responds to it.
Messages are sent to ports - message queues identified by a unique number in the operating system domain. Port can belong to specific process or to the kernel.
Ports can be associated with specific paths and registered in the operating system namespace. There is a system call used to lookup for a port number using provided path.
The port registering functionality is used by operating system servers (e.g. device drivers, file servers) as a basic method of integration with the other operating system components. For example if a thread working in the process context opens the file given by specific path, it indirectly lookups for the port of the file server handling this object and finally receives the
oid_t(port, id) structure identifying the file on the server. It is done because the file server handling particular file during start registers its port in the namespace handled by the other server or by the kernel. File server mount its namespace to the existing namespace handled by existing file servers. The namespace mounting functionality is presented on the following picture.
In case of device drivers they registers special names in the namespace and associate them with the specific
When program opens the file registered by a device driver it receives
oid pointed directly to the device driver server, so all communication is redirected to this server. This idea has been briefly presented on following figure.
Kernel implements message passing by the following functions.
extern int proc_send(u32 port, msg_t *msg); extern int proc_recv(u32 port, msg_t *msg, unsigned int *rid); extern int proc_respond(u32 port, msg_t *msg, unsigned int rid);
msg_t identifies message type and and consist of two main parts - input part and output part.
Input part points to the input buffer and defines its size. It contains also a small buffer for passing the message application header. The output part has symmetrical architecture to input buffer. It contains the pointer to output buffer, output buffer data length and buffer for output application header.
When message is sent by the
proc_send function the sending thread is suspended until the receiving thread executes
proc_recv function, reads data from input buffer, writes the final answer to the output buffer and executes
rid word identifies the receiving context and should be provided to the
proc_respond function. There is possible to execute a lot of instruction between receiving and responding procedure. Responding function is used to wake-up the sending thread and inform it that data in output buffer are completed.
To prevent copying of big data blocks over the kernel when communication goes between threads assigned to separate processes special optimization is introduced. When message is received by the receiving thread input and output buffers are transparently mapped into the receiver address space. To prevent interference with other data, if any of these buffers is not aligned with the page, the heading or tailing part of this buffer is copied to the newly allocated page mapped instead of the original page. When receiving thread responses to the message the buffers are unmapped and heading or tailing parts are copied to the original page located in sender address space. This technique is briefly presented on following figure.
There is another type of optimization. If input or output data size is lower then page size and data fits into the buffer used for application header passing the data is copied instead of using virtual memory capabilities which provide extra overhead for small messages.