Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Sauermann J.Realtime operating systems.Concepts and implementation of microkernels for embedded systems.1997.pdf
Скачиваний:
29
Добавлен:
23.08.2013
Размер:
1.32 Mб
Скачать

26

2.5 Queues

 

 

2.5Queues

Although semaphores provide the most powerful data structure for preemptive multitasking, they are only occasionally used explicitly. More often, they are hidden by another data structure called queues. Queues, also called FIFOs (first in, first out), are buffers providing at least two functions: Put() and Get(). The size of the items stored in a queue may vary, thus Queue is best implemented as a template class. The number of items may vary as well, so the constructor of the class will take the desired length as an argument.

2.5.1 Ring Buffers

The simplest form of a queue is a ring buffer. A consecutive part of memory, referred to as Buffer, is allocated, and two variables, the GetIndex and the PutIndex, are initialized to 0, thus pointing to the beginning of the memory space. The only operation performed on the GetIndex and the PutIndex is incrementing them. If they happen to exceed the end of the memory, they are reset to the beginning. This wrapping around at the end turns the straight piece of memory into a ring. The buffer is empty if and only if GetIndex = PutIndex. Otherwise, the PutIndex is always ahead of the GetIndex (although the PutIndex may be less than the GetIndex if the PutIndex already wrapped around at the end, while the GetIndex did not wrap around yet). In Figure 2.13, a ring buffer is shown both as straight memory and as a logical ring.

2. Concepts

27

 

 

Buffer

Item

Item

Item

Item

 

Item

Item

 

 

 

 

 

 

 

 

Item

Item

Get

Put

Item

Item

Get

Item

Put

 

Item

Item

Item

FIGURE 2.13 Ring Buffer

The algorithm for Put(), which takes an item as its arguments and puts it into the ring buffer, is as follows:

Wait as long as the Buffer is full, or return Error indicating overflow

Buffer[PutIndex] = Item

PutIndex = (PutIndex + 1) modulo BufferSize

(increment

 

 

PutIndex, wrap

 

 

around at end)

Get(), which removes the next item from the ring buffer and returns it, has the following algorithm:

Wait as long as Buffer is empty, or return Error indicating underflow

Item = Buffer[GettIndex]

GetIndex = (GetIndex + 1) modulo BufferSize(increment GetIndex, wrap around at end)

Return Item

28

2.5 Queues

 

 

In practice, an empty buffer is much more likely than a buffer overflow. In embedded systems, an empty buffer is a sign of proper design, while a full buffer usually shows that something is wrong. So Get() and Put() can also be compared to a bank account, which tends to be empty rather than overflow.

Assume that we don not want to return an error condition on full or empty buffers. There are good reasons not to return an error condition, since this condition is likely to disappear again, and the response to such an error condition will most often be a retry of the Put() or Get(). That is, we assume we want to wait. The simplest (and worst) approach is again busy wait:

For the Get() function:

While GetIndex = PutIndex

Do Nothing

(i.e. waste time)

For the Put() function:

While GetIndex = (PutIndex + 1) modulo BufferSize

Do Nothing

(i.e. was time)

The note on bank accounts and the term busy wait should have reminded you of semaphores.

2.5.2 Ring Buffer with Get Semaphore

The basic idea is to consider the items in a buffer as resources. I have seen this idea for the first time in an operating system called MIRAGE about twenty years ago. It was used for interrupt-driven character I/O.

In addition to the GetIndex and PutIndex variables, we add a semaphore called GetSemaphore, which represents the items in the buffer. As GetIndex and PutIndex are initialized to 0 (that is, the buffer is initially empty), this semaphore is initialized with its Counter variable set to 0.

For each Put(), a V() call is made to this semaphore after the item has been inserted into the buffer. This indicates that another item is available.

Wait as long as the Buffer is full, or return Error indicating overflow

Buffer[PutIndex] = Item

PutIndex = (PutIndex + 1) modulo BufferSize(increment PutIndex, wrap around at end)

Call V() for GetSemaphore

2. Concepts

29

 

 

For each Get(), a P() call is made before removing an item from the buffer. If there are no more items in the buffer, then the task performing the Get() and thus the P() is blocked until someone uses Put() and thus V() to insert an item.

Call P() for GetSemaphore

Item = Buffer[GettIndex]

GetIndex = (GetIndex + 1) modulo BufferSize(increment GetIndex, wrap around at end)

Return Item

2.5.3 Ring Buffer with Put Semaphore

Instead of considering the items that are already inserted as resources, we could as well consider the free space in the buffer as resources. In addition to the GetIndex and PutIndex variables for the plain ring buffer, we add a semaphore called PutSemaphore, which represents the free space in the buffer. As GetIndex and PutIndex are initialized to 0 (that is, the buffer is initially empty), this semaphore (in contrast to the GetSemaphore) is initialized with its Counter variable set to BufferSize.

For each Put(), a P() call is made to this semaphore before the item is inserted into the buffer and thus buffer space is reduced. If there is no more free space in the buffer, then the task performing the Put() and thus the P() is blocked until someone uses Get() and thus V() to increase the space again.

Call P() for PutSemaphore

Buffer[PutIndex] = Item

PutIndex = (PutIndex + 1) modulo BufferSize(increment PutIndex, wrap around at end)

For each Get(), a P() call is made after removing an item from the buffer, indicating another free position in the buffer.

Wait as long as Buffer is empty, or return Error indicating underflow

Item = Buffer[GettIndex]

GetIndex = (GetIndex + 1) modulo BufferSize(increment GetIndex, wrap around at end)

Call V() for PutSemaphore

Return Item

This scheme is used less often than the ring buffer with Get semaphore. To understand why, let us consider a task which communicates with an interrupt-