187 lines
5.6 KiB
ReStructuredText
187 lines
5.6 KiB
ReStructuredText
.. _pipes_v2:
|
|
|
|
Pipes
|
|
#####
|
|
|
|
A :dfn:`pipe` is a kernel object that allows a thread to send a byte stream
|
|
to another thread. Pipes can be used to transfer chunks of data in whole
|
|
or in part, and either synchronously or asynchronously.
|
|
|
|
.. contents::
|
|
:local:
|
|
:depth: 2
|
|
|
|
Concepts
|
|
********
|
|
|
|
The pipe can be configured with a ring buffer which holds data that has been
|
|
sent but not yet received; alternatively, the pipe may have no ring buffer.
|
|
|
|
Any number of pipes can be defined. Each pipe is referenced by its memory
|
|
address.
|
|
|
|
A pipe has the following key property:
|
|
|
|
* A **size** that indicates the size of the pipe's ring buffer. Note that a
|
|
size of zero defines a pipe with no ring buffer.
|
|
|
|
A pipe must be initialized before it can be used. The pipe is initially empty.
|
|
|
|
Data can be synchronously **sent** either in whole or in part to a pipe by a
|
|
thread. If the specified minimum number of bytes can not be immediately
|
|
satisfied, then the operation will either fail immediately or attempt to send
|
|
as many bytes as possible and then pend in the hope that the send can be
|
|
completed later. Accepted data is either copied to the pipe's ring buffer
|
|
or directly to the waiting reader(s).
|
|
|
|
Data can be asynchronously **sent** in whole using a memory block to a pipe by
|
|
a thread. Once the pipe has accepted all the bytes in the memory block, it will
|
|
free the memory block and may give a semaphore if one was specified.
|
|
|
|
Data can be synchronously **received** from a pipe by a thread. If the specified
|
|
minimum number of bytes can not be immediately satisfied, then the operation
|
|
will either fail immediately or attempt to receive as many bytes as possible
|
|
and then pend in the hope that the receive can be completed later. Accepted
|
|
data is either copied from the pipe's ring buffer or directly from the
|
|
waiting sender(s).
|
|
|
|
.. note::
|
|
The kernel does NOT allow for an ISR to send or receive data to/from a
|
|
pipe even if it does not attempt to wait for space/data.
|
|
|
|
Implementation
|
|
**************
|
|
|
|
A pipe is defined using a variable of type :c:type:`struct k_pipe` and an
|
|
optional character buffer of type :c:type:`unsigned char`. It must then be
|
|
initialized by calling :cpp:func:`k_pipe_init()`.
|
|
|
|
The following code defines and initializes an empty pipe that has a ring
|
|
buffer capable of holding 100 bytes and is aligned to a 4-byte boundary.
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
unsigned char __aligned(4) my_ring_buffer[100];
|
|
struct k_pipe my_pipe;
|
|
|
|
k_pipe_init(&my_pipe, my_ring_buffer, sizeof(my_ring_buffer));
|
|
|
|
Alternatively, a pipe can be defined and initialized at compile time by
|
|
calling :c:macro:`K_PIPE_DEFINE`.
|
|
|
|
The following code has the same effect as the code segment above. Observe
|
|
that that macro defines both the pipe and its ring buffer.
|
|
|
|
.. code-block:: c
|
|
|
|
K_PIPE_DEFINE(my_pipe, 100, 4);
|
|
|
|
Writing to a Pipe
|
|
=================
|
|
|
|
Data is added to a pipe by calling :cpp:func:`k_pipe_put()`.
|
|
|
|
The following code builds on the example above, and uses the pipe to pass
|
|
data from a producing thread to one or more consuming threads. If the pipe's
|
|
ring buffer fills up because the consumers can't keep up, the producing thread
|
|
waits for a specified amount of time.
|
|
|
|
.. code-block:: c
|
|
|
|
struct message_header {
|
|
...
|
|
};
|
|
|
|
void producer_thread(void)
|
|
{
|
|
unsigned char *data;
|
|
size_t total_size;
|
|
size_t bytes_written;
|
|
int rc;
|
|
...
|
|
|
|
while (1) {
|
|
/* Craft message to send in the pipe */
|
|
data = ...;
|
|
total_size = ...;
|
|
|
|
/* send data to the consumers */
|
|
rc = k_pipe_put(&my_pipe, data, total_size, &bytes_written,
|
|
sizeof(struct message_header), K_NO_WAIT);
|
|
|
|
if (rc < 0) {
|
|
/* Incomplete message header sent */
|
|
...
|
|
} else if (bytes_written < total_size) {
|
|
/* Some of the data was sent */
|
|
...
|
|
} else {
|
|
/* All data sent */
|
|
...
|
|
}
|
|
}
|
|
}
|
|
|
|
Reading from a Pipe
|
|
===================
|
|
|
|
Data is read from the pipe by calling :cpp:func:`k_pipe_get()`.
|
|
|
|
The following code builds on the example above, and uses the pipe to
|
|
process data items generated by one or more producing threads.
|
|
|
|
.. code-block:: c
|
|
|
|
void consumer_thread(void)
|
|
{
|
|
unsigned char buffer[120];
|
|
size_t bytes_read;
|
|
struct message_header *header = (struct message_header *)buffer;
|
|
|
|
while (1) {
|
|
rc = k_pipe_get(&my_pipe, buffer, sizeof(buffer), &bytes_read,
|
|
sizeof(header), K_MSEC(100));
|
|
|
|
if ((rc < 0) || (bytes_read < sizeof (header))) {
|
|
/* Incomplete message header received */
|
|
...
|
|
} else if (header->num_data_bytes + sizeof(header) > bytes_read) {
|
|
/* Only some data was received */
|
|
...
|
|
} else {
|
|
/* All data was received */
|
|
...
|
|
}
|
|
}
|
|
}
|
|
|
|
Suggested uses
|
|
**************
|
|
|
|
Use a pipe to send streams of data between threads.
|
|
|
|
.. note::
|
|
A pipe can be used to transfer long streams of data if desired. However
|
|
it is often preferable to send pointers to large data items to avoid
|
|
copying the data. The kernel's memory map and memory pool object types
|
|
can be helpful for data transfers of this sort.
|
|
|
|
Configuration Options
|
|
*********************
|
|
|
|
Related configuration options:
|
|
|
|
* :option:`CONFIG_NUM_PIPE_ASYNC_MSGS`
|
|
|
|
APIs
|
|
****
|
|
|
|
The following message queue APIs are provided by :file:`kernel.h`:
|
|
|
|
* :c:macro:`K_PIPE_DEFINE`
|
|
* :cpp:func:`k_pipe_init()`
|
|
* :cpp:func:`k_pipe_put()`
|
|
* :cpp:func:`k_pipe_get()`
|
|
* :cpp:func:`k_pipe_block_put()`
|