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

3. Kernel Implementation

59

 

 

3.7Serial Input and Output

The basic model for serial input and output has already been discussed in Section 2.5.3 and presented in Figure 2.14. In principle, the input and output directions are completely independent of each other, except for the software flow control (e.g. XON/XOFF protocol) at the hardware side of the receive buffer, and possibly line editing functions (e.g. echoing of characters) at the task side of the receive buffer.

This section deals with the task side of both the receive and transmit buffers; the hardware side is discussed in Section 3.8. Strictly speaking, the aspects of serial input and output discussed here are not part of the operating system itself. But they are so commonly used that it is appropriate to include them in the kernel.

Several tasks sharing one serial input or output channel is a common source of trouble. A typical example is a router that receives data packets on several serial ports and transmits them (after possibly modifying them) on other serial ports.

??? What is the trouble ???An implementation with three serial ports could be as shown in Figure 3.5.

Rx Buf 0

Rx T0

Packet

Tx T0

Tx Buf 0

Handler

 

 

 

 

Rx Buf 1

Rx T1

 

Tx T1

Tx Buf 1

Rx Buf 2

Rx T2

Packet

Tx T2

Tx Buf 2

Handler

 

 

 

 

 

Queue of idle Packet Handlers

 

 

Packet

Packet

Packet

Packet

Handler

Handler

Handler

Handler

FIGURE 3.5 Serial Router (Version A)

60

3.7 Serial Input and Output

 

 

For each serial port, there is a receive task (RX T0..2) that receives characters from its serial port. If a complete packet is received, the receive task fetches a pointer to an idle packet handler task and sends a message containing the packet to that task. The packet handler task processes the packet and may create other packets that are sent as messages to some of the transmit tasks (Tx T0..2). When a packet handler has finished processing a packet, it puts itself back into the queue of idle packet handlers. The transmit tasks merely send the packets out on their respective serial outputs. In this implementation, each serial input is handled by one task Rx Ti, and each serial output is handled by a task Tx Ti dedicated to that port. The main purpose of these tasks is to maintain atomicity at packet level. That is, these tasks are responsible for assembling and de-assembling sequences of characters into packets and vice versa. Since the receive and transmit tasks are statically bound to their serial ports, there is no conflict between tasks with regard to ports.

Now assume there is some mechanism by which a task can temporarily claim a serial input and output port for a period of time so that no other task can use that port at the same time. Then the number of tasks involved could be reduced as shown in Figure 3.6.

Rx Buf 0

Packet

 

Tx Buf 0

Handler

 

 

 

 

Rx Buf 1

Packet

 

Tx Buf 1

Handler

 

 

 

 

Rx Buf 2

Packet

Packet

Tx Buf 2

Handler

Handler

 

 

Queue of unserved input ports

Packet

Packet

Packet

Packet

Handler

Handler

Handler

Handler

FIGURE 3.6 Serial Router (Version B)

3. Kernel Implementation

61

 

 

At the output side, a packet handler merely claims a serial output port when it needs to transmit a packet. The queue of idle packet handlers has been replaced by a queue of input ports that have no packet handlers assigned; this queue initially contains all serial input ports. A packet handler first gets an unserved input port, so that shortly after start-up of the system each input port is served by a packet handler; the other packet handlers are blocked at the queue for unserved inputs. A packet handler serving an input first claims that input port and starts collecting the characters of the next packet. When a complete packet is received, the packet handler releases the input port (which causes the next idle packet server to take over that port), puts it back into the queue of unserved input ports, and continues processing of the packet. Like in router version A, this scheme schedules the packet handlers between the ports in a fair way. Sometimes, in particular if the serial ports need to have different priorities (e.g. due to different communication speeds), a scheduling on a per-port basis is required. This leads to an even simpler implementation shown in Figure 3.7.

Rx Buf 0

Packet

 

Tx Buf 0

Handler

 

 

 

 

Rx Buf 1

Packet

 

Tx Buf 1

Handler

 

 

 

 

Rx Buf 2

Packet

Packet

Tx Buf 2

Handler

Handler

 

 

FIGURE 3.7 Serial Router (Version C)

With this implementation, one can e.g. assign different priorities to each input port and use different numbers of packet servers. The packet servers queue themselves by claiming the input port, so that the queue of unserved input ports used in version B becomes obsolete. As a consequence, no initialization of that queue is required. The code for the packet handler becomes as simple as that:

Semaphore Port_0_Input, Port_0_Output;

Semaphore Port_1_Input, Port_1_Output;

Semaphore Port_2_Input, Port_2_Output;

void packet_handler_main(Semaphore & Port_i_Input)

{

for (;;)

{

Port_i_Input.P();

62

3.7 Serial Input and Output

 

 

char * Packet = getPacket(port);

Port_i_Input.V();

 

handlePacket(Packet);

// deletes Packet

}

}

The semaphores control the claiming and releasing of the serial input and output ports. Using semaphores explicitly is not very elegant though. First, it must be assured that any task using a serial port is claiming and releasing the corresponding semaphore. Also it is often desirable to have a “dummy” port (such as /dev/nul in UNIX) that behaves like a real serial port. Such a dummy port could be used e.g. to turn logging information on and off. But claiming and releasing dummy ports makes little sense. In general, the actual implementation of a port should be hidden from the interface using the port. Thus for a clean objectoriented design, the semaphores should be maintained by the port rather than by an application using the port. This leads to the kernel implementation of serial input and output described in the following sections.

3.7.1 Channel Numbers

It is convenient to refer to serial ports by channel numbers. In our hardware model, we assumed one DUART with two serial ports, which we call SERIAL_0 and SERIAL_1. These are normally operated in an interrupt-driven manner. Sometimes however, it is required to have a polled operation available, in particular before the interrupt system has been initialized, and in the case of fatal system errors. For achieving this polled operation, the channels

SERIAL_0_POLLED and SERIAL_1_POLLED are provided. Finally, the

DUMMY_SERIAL channel is used when the actual serial output needs to be suppressed.

1

// Channels.hh

 

...

 

 

5

enum Channel {

 

6

SERIAL_0

= 0,

7

SERIAL_1

= 1,

8

SERIAL_0_POLLED

= 4,

9

SERIAL_1_POLLED

= 5,

10

DUMMY_SERIAL

= 8,

11

};

 

Often, one would like to turn the serial output on and off, e.g. for debugging purposes. Therefore, channel variables rather than explicit channels are used:

1 // Channels.hh

...

13extern Channel MonitorIn;

14extern Channel MonitorOut;

15extern Channel ErrorOut;