The MIPC
API is an optional feature that facilitates
message queues in ChorusOS systems. This feature enables an application,
composed of one or more actors, to create a shared communication environment --
often referred to as a message space. Within this message space, actors can
exchange messages efficiently. Supervisor and user actors of the same application
can use MIPC
to exchange messages. Furthermore, messages
may be initially allocated and sent by interrupt handlers to be processed
later by threads.
The MIPC
API includes the following system
calls:
Allocate a message
Free a message
Get a message
Get a message from any declared queues
Retrieve statistics about a message pool
Post a message
Retrieve statistics about a message queue
Remove a message from a queue
Create a message space
Open a message space.
Message queues are designed around the concept of a message space which encapsulates within a single entity:
A set of message pools shared by all actors of the application.
A set of message queues through which these actors exchange messages allocated from the shared message pools.
A message space is a temporary resource that must be created explicitly by one actor within the application. Once created, a message space can be opened by other actors within the application. Actors that have opened the same message space are said to share the message space. A message space is deleted automatically when the actor is deleted.
A message pool is defined by two parameters (message size and number of messages) provided by the application when it creates the message space. The configuration of the set of message pools defined within a message space depends on the application requirements.
A message is an array of bytes which can be structured and used at application level through any appropriate convention. Messages are presented to actors as pointers within their address space.
Messages are posted to message queues belonging to the same message space. All actors sharing a message space can allocate messages from the message pools. In the same way, all actors sharing a message space have send and receive rights on each queue of the message space.
Even though most applications need to create only a single message space, the message queue feature is designed to enable an application to create or open multiple message spaces. However, messages allocated from one message space cannot be sent to a queue of a different message space. The following example illustrates the typical use of message spaces:
The first actor, aware of the overall requirements of the application, creates the message space.
Other actors of the application open the shared message space.
An actor allocates a message from a message pool, and fills it with the data to be sent.
The actor which allocated the message then posts it to the appropriate queue and assigns a priority to the message.
The destination actor obtains the message from the queue. At this point, the message is removed from the queue.
After the destination actor has processed the message, the message can be freed. It is then available to be reallocated by the application. Alternatively, the destination actor may modify the message and post it to another queue.
To make the service as efficient as possible, physical memory is allocated for all messages and data structures of the message space at message space creation. At message space open time, this memory is transparently mapped by the system into the actor address space. Further operations such as posting and receiving a message are performed without any copy.
You can create a message space as follows:
#include <mipc/chMipc.h> int msgSpaceCreate (KnMsgSpaceId spaceGid, unsigned int msgQueueNb, unsigned int msgPoolNb, const KnMsgPool* msgPools);
The spaceGid parameter is an unique global identifier assigned by the application to the message space being created. This identifier is also used by other actors of the application to open the message space. Therefore, the identifier serves to bind actors participating in the application to the same message space. The K_PRIVATEID predefined global identifier indicates that the message space created will be private to the invoking actor. This means that its queues and message pools will only be accessible to threads executing within this actor. No other actor will be able to open the message space. The message space is described by the following three parameters:
msqgQueueNb indicates how many queues must be created within the message space. Each queue in the message space is then designated by its index within the set of queues. This may vary from 0 to msgQueueNb - 1.
msgPoolNb is the number of message pools to be created in the message space.
msgPools is a pointer to an array of msgPoolNb pool descriptions. Each pool is described by a KnMsgPool data structure, which includes the following information:
msgSize, which defines the size of each message belonging to the pool
msgNumber, which defines how many messages of msgSize bytes must be created in this pool
Figure 11-1 shows an example of a message space recently created by an actor.
The created message space is assigned a local identifier which is returned to the invoking actor as the return value of the msgSpaceCreate(2K). The scope of this local identifier is the invoking actor.
A message space may be opened by other actors through the following call:
#include <mipc/chMipc.h> int msgSpaceOpen(KnMsgSpaceId spaceGid);
The message space assigned with the spaceGid unique global identifier must have been created previously by a call to msgSpaceCreate(2K). A local identifier is returned to the invoking actor. This message space local identifier can then be used to manipulate messages and queues within the message space. Figure 11-2 shows an example of a message space recently opened by a second actor.
A message may be allocated by the following call:
#include <mipc/chMipc.h> int msgAllocate(int spaceLid, unsigned int poolIndex, unsigned int msgSize, KnTimeVal* waitLimit, char** msgAddr);
msgAllocate(2K) attempts to allocate a message from the appropriate pool of the message space identified by the spaceLid return value of a previous call to msgSpaceOpen(2K), or to msgSpaceCreate(2K). If poolIndex is not set to K_ANY_MSGPOOL, the allocated message is the first free (unallocated) message of the pool defined by poolIndex, regardless of the value specified by the msgSize parameter. Otherwise, if poolIndex is set to K_ANY_MSGPOOL, the message is allocated from the first pool for which the message size fits the requested msgSize. In this context, first pool means the pool with the lowest index in the set of pools defined at message space creation time. If the pool is empty, no attempt is made to allocate a message from another pool.
If the message pool is empty (all messages have been allocated and none are free), msgAllocate(2K) will block, waiting for a message in the pool to be freed. The invoking thread is blocked until the wait condition defined by waitLimit expires.
If successful, the address of the allocated message is stored at the location defined by msgAddr. The returned address is the address of the message within the address space of the actor. Remember that a message space is mapped to the address space of the actors sharing it. However, the message spaces (and therefore the messages themselves), may be mapped to different addresses in different actors. This is especially true for message spaces shared between supervisor and user actors.
Figure 11-3 illustrates two actors allocating two messages from two different pools of the same message space.
After a message has been allocated and initialized by the application, it may be posted to a message queue with:
#include <mipc/chMipc.h> int msgPut(int spaceLid, unsigned int queueIndex, char* msg, unsigned int prio);
msgPut(2K) posts the message (the address of which is msg) to the message queue queueIndex within the message space (the local identifier of which is spaceLid). The message has already been allocated previously by a call to msgAllocate(2K). The message is inserted into the queue according to its priority, prio. Messages with a high priority are taken from the queue first.
A message is posted to a queue without any message copy. Additionally, the message may be posted within an interrupt handler, or with preemption disabled.
Figure 11-4 illustrates how the actors from the previous figure post their messages to different queues.
To obtain a message from a queue (if any) use the following call:
#include <mipc/chMipc.h> int msgGet(int spaceLid, unsigned int queueIndex, KnTimeVal* waitLimit, char** msgAddr, KnUniqueId* srcActor);
msgGet(2K) enables the invoking thread to get the first message with the highest priority pending in the message queue queueIndex, within the message space whose local identifier is spaceLid. Messages with equal priority are posted and delivered in a first-in first-out order.
The address of the message delivered to the invoking thread is returned at the location defined by the msgAddr parameter. If no message is pending, the invoking thread is blocked until a message is sent to the message queue, or until the time-out (as defined by the expiration of the waitLimit parameter).
If the srcActor parameter is non-NULL, it points to a location where the unique identifier of the actor is to be stored. The actor that posted the message is referred to as the source actor).
No data copy is performed to deliver the message to the invoking thread. Multiple threads can be blocked, waiting in the same message queue. With the msgGetAny() call it is possible for one thread to wait for message arrival on all message queues, and to obtain the first posted message from any queue. It is not possible to define a subset of queues and to wait for a message to arrive on this subset. Consequently, msgGet() allows you to wait for a message on a queue described by an index held in its second argument, and msgGetAny() allows you to wait for a message from all queues.
The msgGetAny() call is defined as follows:
int msgGetAny(int spaceLid, unsigned int* msgQueueId, KnTimeVal* waitLimit, char** msgAddr, KnUniqueId* srcActor);
Figure 11-5 illustrates previously created actors receiving messages from queues.
A message that is of no further use to the application can be returned to its pool of messages and will be available for reallocation with the following call:
#include <mipc/chMipc.h> int msgFree(int spaceLid, char* msg);
To retrieve information about a specific message queue or message pool, use the following call:
#include <mipc/chMipc.h> int msgPoolStat (int spaceLid, unsigned int msgPoolId, KnMsgPoolStat* stat); int msgQueueStat(int spaceLid, unsigned int msgQueueId, KnMsgQueueStat* stat);
Information about a message pool is stored in the KnMsgPoolStat structure which contains:
the sizes of the messages in the pool.
the number of messages in the pool.
the number of free messages in the pool (messages that can be allocated using msgAllocate).
Information about a message queue is stored in the KnMsgQueueStat structure which contains:
the number of messages currently waiting in the queue.
msgFree and msgNumber may be invalid after the next time slice or thread schedule because other threads or applications have access to the message space.