kernel/services/data_passing/pipes.html · kernel/services/data_passing/pipes.rst 공식 원문 ↗

Zephyr 3.7.0 · Kernel · Data Passing

Pipe

선택적 ring buffer를 통해 byte stream 전체 또는 일부를 동기로 전달하고 최소 성공 전송량을 지정합니다.

Official pathkernel/services/data_passing/pipes.html
Source filekernel/services/data_passing/pipes.rst
Source versionZephyr 3.7.0
TranslationFull · reviewed

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

Part 1

요약·해설

Buffered·unbuffered pipe, direct sender/reader transfer, min_xfer와 timeout 결과, buffer-only·whole-pipe flush 차이를 설명합니다.

Stream

byte-oriented

Transfer

partial · minimum

Flush

buffer · entire pipe

Part 2

접을 수 있는 영어 원문 전체

영어 원문 전체 펼치기
원문 SHA-256 81EE6C563FBED4D0B22DC700B70B58DBCD989EB2FEB48D7E02C617E3A34DDE87
.. _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 synchronously transfer chunks of data
in whole or in part.

.. 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 (limited only by available RAM). 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 is 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 is 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).

Data may also be **flushed** from a pipe by a thread. Flushing can be performed
either on the entire pipe or on only its ring buffer. Flushing the entire pipe
is equivalent to reading all the information in the ring buffer **and** waiting
to be written into a giant temporary buffer which is then discarded. Flushing
the ring buffer is equivalent to reading **only** the data in the ring buffer
into a temporary buffer which is then discarded. Flushing the ring buffer does
not guarantee that the ring buffer will stay empty; flushing it may allow a
pended writer to fill the ring buffer.

.. note::
    Flushing does not in practice allocate or use additional buffers.

.. note::
    The kernel does allow for an ISR to flush a pipe from an ISR. It also
    allows it to send/receive data to/from one provided it does not attempt
    to wait for space/data.

Implementation
**************

A pipe is defined using a variable of type :c:struct:`k_pipe` and an
optional character buffer of type ``unsigned char``. It must then be
initialized by calling :c: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 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 :c: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 :c: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 */
                ...
            }
        }
    }

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.

Flushing a Pipe's Buffer
========================

Data is flushed from the pipe's ring buffer by calling
:c:func:`k_pipe_buffer_flush`.

The following code builds on the examples above, and flushes the pipe's
buffer.

.. code-block:: c

    void monitor_thread(void)
    {
        while (1) {
            ...
            /* Pipe buffer contains stale data. Flush it. */
            k_pipe_buffer_flush(&my_pipe);
            ...
        }
    }

Flushing a Pipe
===============

All data in the pipe is flushed by calling :c:func:`k_pipe_flush`.

The following code builds on the examples above, and flushes all the
data in the pipe.

.. code-block:: c

    void monitor_thread(void)
    {
        while (1) {
            ...
            /* Critical error detected. Flush the entire pipe to reset it. */
            k_pipe_flush(&my_pipe);
            ...
        }
    }


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. Copying large data items will negatively impact interrupt latency
    as a spinlock is held while copying that data.


Configuration Options
*********************

Related configuration options:

* :kconfig:option:`CONFIG_PIPES`

API Reference
*************

.. doxygengroup:: pipe_apis

Part 3

한국어 전문 번역

Pipe는 한 thread가 다른 thread로 byte stream을 보내는 kernel object입니다. Data chunk 전체 또는 일부를 동기 방식으로 전달할 수 있습니다.

개념

Pipe는 전송됐지만 아직 수신되지 않은 data를 보관하는 ring buffer를 가질 수도 있고 buffer 없이 동작할 수도 있습니다. 사용 가능한 RAM 범위에서 여러 pipe를 정의하고 memory 주소로 참조합니다. 핵심 속성인 size는 ring buffer 크기이며 0이면 ring buffer가 없는 pipe입니다. 첫 사용 전 초기화하면 비어 있습니다.

Thread가 pipe에 data를 동기로 보낼 때 지정한 최소 byte 수를 즉시 만족하지 못하면 곧바로 실패하거나 가능한 만큼 먼저 보내고 나머지 완료를 기다리며 pending됩니다. 받아들인 data는 ring buffer로 복사하거나 기다리는 reader에 직접 전달합니다.

수신도 동기 방식입니다. 최소 byte 수를 즉시 만족하지 못하면 실패하거나 가능한 만큼 읽고 나머지를 기다립니다. Data는 ring buffer에서 복사하거나 기다리는 sender에서 직접 전달됩니다.

Thread는 pipe 전체 또는 ring buffer만 flush할 수 있습니다. 전체 flush는 ring buffer data와 쓰려고 기다리는 data를 모두 거대한 임시 buffer로 읽은 뒤 버리는 것과 같습니다. Buffer flush는 현재 ring buffer data만 임시 buffer로 읽어 버리는 것과 같으며, 빈 공간 때문에 pending writer가 다시 채울 수 있으므로 buffer가 계속 빈 상태라는 보장은 없습니다.

구현

Pipe 정의

struct k_pipe variable과 선택적인 unsigned char buffer를 정의한 뒤 k_pipe_init()으로 초기화합니다. 다음 pipe는 4-byte boundary에 정렬된 100-byte ring buffer를 가집니다.

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));

K_PIPE_DEFINE macro는 pipe와 ring buffer를 함께 compile time에 정의하고 초기화합니다.

K_PIPE_DEFINE(my_pipe, 100, 4);

Pipe에 쓰기

k_pipe_put()으로 data를 추가합니다. 다음 producer는 최소 message header 크기를 즉시 쓸 수 있어야 성공으로 취급하며, 전체 data보다 적게 기록된 부분 전송(partial transfer)도 별도로 처리합니다.

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 */
            ...
        }
    }
}

Pipe에서 읽기

k_pipe_get()으로 data를 읽습니다. 다음 consumer는 100 ms 안에 최소 header 크기를 받도록 요청하고, header 미완성·부분 payload·전체 message를 각각 구분합니다.

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 */
            ...
        }
    }
}

Byte stream을 thread 사이에 보낼 때 pipe를 사용합니다.

Pipe buffer flush

k_pipe_buffer_flush()은 pipe ring buffer의 현재 data를 버립니다. 다음 monitor는 stale data가 있다고 판단할 때 buffer만 비웁니다.

void monitor_thread(void)
{
    while (1) {
        ...
        /* Pipe buffer contains stale data. Flush it. */
        k_pipe_buffer_flush(&my_pipe);
        ...
    }
}

Pipe 전체 flush

k_pipe_flush()는 buffer와 pending writer data까지 pipe의 모든 data를 버립니다. 다음 monitor는 치명적 오류 뒤 pipe를 reset하는 용도로 사용합니다.

void monitor_thread(void)
{
    while (1) {
        ...
        /* Critical error detected. Flush the entire pipe to reset it. */
        k_pipe_flush(&my_pipe);
        ...
    }
}

권장 용도

Thread 사이에서 data stream을 보낼 때 pipe를 사용합니다.

설정 옵션

CONFIG_PIPES를 활성화해야 pipe 기능을 사용할 수 있습니다.

API 참고: pipe_apis

K_PIPE_DEFINE(name, pipe_buffer_size, pipe_align)

Pipe와 선택적 ring buffer를 정적으로 정의하고 초기화합니다. 정의 module 밖에서는 아래 extern declaration으로 접근합니다.

extern struct k_pipe <name>;

매개변수

  • name: pipe 이름
  • pipe_buffer_size: ring buffer 크기(byte), buffer가 없으면 0
  • pipe_align: ring buffer 정렬값, 2의 거듭제곱
k_pipe_init(pipe, buffer, size)

Pipe object를 첫 사용 전에 초기화합니다.

매개변수

  • pipe: pipe 주소
  • buffer: ring buffer 주소, 사용하지 않으면 NULL
  • size: ring buffer 크기(byte), 사용하지 않으면 0
k_pipe_cleanup(pipe)

k_pipe_alloc_init()이 동적으로 할당한 buffer를 해제합니다. 정적 buffer에는 아무 작업도 하지 않습니다.

매개변수

  • pipe: pipe 주소

반환값

  • 0: 성공
  • -EAGAIN: 정리할 동적 buffer가 없음
k_pipe_alloc_init(pipe, size)

호출 thread resource pool에서 buffer를 할당하고 초기화합니다. 초기화되지 않은 pipe에만 호출해야 합니다.

매개변수

  • pipe: pipe 주소
  • size: ring buffer 크기(byte), buffer가 없으면 0

반환값

  • 0: 성공
  • -ENOMEM: memory 할당 실패
k_pipe_put(pipe, data, bytes_to_write, bytes_written, min_xfer, timeout)

Pipe에 최대 bytes_to_write byte를 씁니다. 실제 기록량은 bytes_written으로 돌려주며, 성공 조건은 최소 min_xfer byte 기록입니다.

매개변수

  • pipe: pipe 주소
  • data: 쓸 data 주소
  • bytes_to_write: 쓸 data 크기
  • bytes_written: 실제 기록 byte 수를 받을 주소
  • min_xfer: 최소 기록 byte 수
  • timeout: 기록을 기다릴 시간, K_NO_WAIT 또는 K_FOREVER

반환값

  • 0: min_xfer 이상 기록
  • -EIO: 기다리지 않고 0 byte 기록
  • -EAGAIN: timeout 만료, 0부터 min_xfer-1 byte 기록
k_pipe_get(pipe, data, bytes_to_read, bytes_read, min_xfer, timeout)

Pipe에서 최대 bytes_to_read byte를 읽습니다. 실제 읽은 양은 bytes_read로 돌려주며, 성공 조건은 최소 min_xfer byte 수신입니다.

매개변수

  • pipe: pipe 주소
  • data: 읽은 data를 둘 주소
  • bytes_to_read: 최대 읽기 byte 수
  • bytes_read: 실제 읽은 byte 수를 받을 주소
  • min_xfer: 최소 읽기 byte 수
  • timeout: data를 기다릴 시간, K_NO_WAIT 또는 K_FOREVER

반환값

  • 0: min_xfer 이상 읽음
  • -EINVAL: 잘못된 매개변수
  • -EIO: 기다리지 않고 0 byte 읽음
  • -EAGAIN: timeout 만료, 0부터 min_xfer-1 byte 읽음
k_pipe_read_avail(pipe)

현재 pipe ring buffer에서 읽을 수 있는 byte 수를 질의합니다. Unbuffered pipe는 0입니다.

매개변수

  • pipe: pipe 주소

반환값

  • 0..size: 읽기 가능한 byte 수
k_pipe_write_avail(pipe)

현재 pipe ring buffer에 쓸 수 있는 byte 수를 질의합니다. Unbuffered pipe는 0입니다.

매개변수

  • pipe: pipe 주소

반환값

  • 0..size: 쓰기 가능한 byte 수
k_pipe_flush(pipe)

Ring buffer data와 pipe에 쓰려고 기다리는 모든 data를 읽어 버린 것처럼 전체 pipe를 비웁니다. 대기 중이던 writer는 pending 상태에서 풀립니다.

매개변수

  • pipe: pipe 주소
k_pipe_buffer_flush(pipe)

Pipe 내부 ring buffer에 현재 들어 있는 data만 버립니다. 빈 공간을 얻은 pending writer가 즉시 buffer를 다시 채울 수 있습니다.

매개변수

  • pipe: pipe 주소
struct k_pipe

Pipe ring buffer와 reader/writer 대기 상태를 나타내는 kernel 구조체입니다.

k_pipe::buffer

Pipe buffer 주소이며 NULL일 수 있습니다.

k_pipe::size

Buffer 크기입니다.

k_pipe::bytes_used

Buffer에서 사용 중인 byte 수입니다.

k_pipe::read_index

Buffer에서 다음에 읽을 index입니다.

k_pipe::write_index

Buffer에서 다음에 쓸 index입니다.

k_pipe::lock

Pipe 상태를 보호하는 synchronization lock입니다.

k_pipe::readers

Reader wait queue입니다.

k_pipe::writers

Writer wait queue입니다.

k_pipe::flags

Wait queue와 buffer 상태 flag입니다.

Source

출처

영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.