· obtaining message queue information.
7.6.1 Creating and Deleting Message Queues
Message queues can be created and deleted by using two simple calls, as shown in Table 7.1.
Table 7.1: Message queue creation and deletion operations.
Operation |
Description |
Create |
Creates a message queue |
Delete |
Deletes a message queue |
When created, message queues are treated as global objects and are not owned by any particular task. Typically, the queue to be used by each group of tasks or ISRs is assigned in the design.
When creating a message queue, a developer needs to make some initial decisions about the length of the message queue, the maximum size of the messages it can handle, and the waiting order for tasks when they block on a message queue.
Deleting a message queue automatically unblocks waiting tasks. The blocking call in each of these tasks returns with an error. Messages that were queued are lost when the queue is deleted.
7.6.2 Sending and Receiving Messages
The most common uses for a message queue are sending and receiving messages. These operations are performed in different ways, some of which are listed in Table 7.2.
Table 7.2: Sending and receiving messages.
Operation |
Description |
Send |
Sends a message to a message queue |
Receive |
Receives a message from a message queue |
Broadcast |
Broadcasts messages |
Sending Messages
When sending messages, a kernel typically fills a message queue from head to tail in FIFO order, as shown in Figure 7.4. Each new message is placed at the end of the queue.
Figure 7.4: Sending messages in FIFO or LIFO order.
Many message-queue implementations allow urgent messages to go straight to the head of the queue. If all arriving messages are urgent, they all go to the head of the queue, and the queuing order effectively becomes last-in/first-out (LIFO). Many message-queue implementations also allow ISRs to send messages to a message queue. In any case, messages are sent to a message queue in the following ways:
· not block (ISRs and tasks),
· block with a timeout (tasks only), and
· block forever (tasks only).
At times, messages must be sent without blocking the sender. If a message queue is already full, the send call returns with an error, and the task or ISR making the call continues executing. This type of approach to sending messages is the only way to send messages from ISRs, because ISRs cannot block.
Most times, however, the system should be designed so that a task will block if it attempts to send a message to a queue that is full. Setting the task to block either forever or for a specified timeout accomplishes this step. (Figure 7.5). The blocked task is placed in the message queue’s task-waiting list, which is set up in either FIFO or priority-based order.
Figure 7.5: FIFO and priority-based task-waiting lists.
In the case of a task set to block forever when sending a message, the task blocks until a message queue element becomes free (e.g., a receiving task takes a message out of the queue). In the case of a task set to block for a specified time, the task is unblocked if either a queue element becomes free or the timeout expires, in which case an error is returned.
Receiving Messages
As with sending messages, tasks can receive messages with different blocking policies-the same way as they send them-with a policy of not blocking, blocking with a timeout, or blocking forever. Note, however, that in this case, the blocking occurs due to the message queue being empty, and the receiving tasks wait in either a FIFO or prioritybased order. The diagram for the receiving tasks is similar to Figure 7.5, except that the blocked receiving tasks are what fills the task list.
For the message queue to become full, either the receiving task list must be empty or the rate at which messages are posted in the message queue must be greater than the rate at which messages are removed. Only when the message queue is full does the task-waiting list for sending tasks start to fill. Conversely, for the task-waiting list for receiving tasks to start to fill, the message queue must be empty.
Messages can be read from the head of a message queue in two different ways:
· destructive read, and
· non-destructive read.
In a destructive read, when a task successfully receives a message from a queue, the task permanently removes the message from the message queue’s storage buffer. In a non-destructive read, a receiving task peeks at the message at the head of the queue without removing it. Both ways of reading a message can be useful; however, not all kernel implementations support the non-destructive read.
Some kernels support additional ways of sending and receiving messages. One way is the example of peeking at a message. Other kernels allow broadcast messaging, explained later in this chapter.
7.6.3 Obtaining Message Queue Information
Obtaining message queue information can be done from an application by using the operations listed in Table 7.3.
Table 7.3: Obtaining message queue information operations.
Operation |
Description |
Show queue info |
Gets information on a message queue |
Show queue’s task-waiting list |
Gets a list of tasks in the queue’s task-waiting list |
Different kernels allow developers to obtain different types of information about a message queue, including the message queue ID, the queuing order used for blocked tasks (FIFO or priority-based), and the number of messages queued. Some calls might even allow developers to get a full list of messages that have been queued up.
As with other calls that get information about a particular kernel object, be careful when using these calls. The information is dynamic and might have changed by the time it’s viewed. These types of calls should only be used for debugging purposes.
7.7 Typical Message Queue Use
The following are typical ways to use message queues within an application:
· non-interlocked, one-way data communication,
· interlocked, one-way data communication,
· interlocked, two-way data communication, and
· broadcast communication.
Note that this is not an exhaustive list of the data communication patterns involving message queues. The following sections discuss each of these simple cases.
7.7.1 Non-Interlocked, One-Way Data Communication
One of the simplest scenarios for message-based communications requires a sending task (also called the message source), a message queue, and a receiving task (also called a message sink), as illustrated in Figure 7.6.
Figure 7.6: Non-interlocked, one-way data communication.
This type of communication is also called non-interlocked (or loosely coupled), one-way data communication. The activities of tSourceTask and tSinkTask are not synchronized. TSourceTask simply sends a message; it does not require acknowledgement from tSinkTask.
The pseudo code for this scenario is provided in Listing 7.1.
Читать дальше