管道:字节流与新版 API
本页依据 文档基线与后续更新 中记录的 Zephyr main 快照更新。
管道传递字节流,不保存消息边界。它可使用环形缓冲区,也可在缓冲区大小为 0 时由 发送者和接收者直接配合传输。应用需要自己定义帧长度、编码、结束条件和错误恢复。
新版接口与旧教程差异
当前公共 API 使用下面的四参数读写接口:
int k_pipe_write(struct k_pipe *pipe, const uint8_t *data,
size_t len, k_timeout_t timeout);
int k_pipe_read(struct k_pipe *pipe, uint8_t *data,
size_t len, k_timeout_t timeout);
返回非负值表示实际传输的字节数,负值表示错误。原教程中的
k_pipe_put/get、bytes_written/bytes_read 输出参数和 min_xfer 语义
不适用于这一接口,不能只替换函数名。
定义与最小示例
下面是单线程使用有缓冲管道的完整示例,先写入 4 字节再读出;环形缓冲区足以容纳全部数据。
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
K_PIPE_DEFINE(byte_stream, 64, 4);
int main(void)
{
const uint8_t tx[] = { 1, 2, 3, 4 };
uint8_t rx[sizeof(tx)];
int written = k_pipe_write(&byte_stream, tx, sizeof(tx), K_NO_WAIT);
if (written != sizeof(tx)) {
printk("write result: %d\n", written);
return 0;
}
int received = k_pipe_read(&byte_stream, rx, sizeof(rx), K_NO_WAIT);
printk("read result: %d\n", received);
return 0;
}
运行时初始化使用 k_pipe_init()。不需要内部缓冲区时传入 NULL, 0,
此时应由不同线程完成双方通信,不能期待同一线程先完成阻塞写入后再去读取。
部分传输
一次调用可能只完成部分请求,必须按返回的长度移动指针。 下面的辅助函数用于线程之间已经启动接收者的场景,它返回已写长度并保留错误:
static int send_bytes(struct k_pipe *pipe, const uint8_t *data,
size_t size, size_t *done)
{
*done = 0;
while (*done < size) {
int rc = k_pipe_write(pipe, data + *done, size - *done,
K_MSEC(100));
if (rc < 0) {
return rc;
}
if (rc == 0) {
return -EAGAIN;
}
*done += (size_t)rc;
}
return 0;
}
此片段需要 <errno.h>。每轮的 100 ms 不是整个消息的总期限;
若业务要求总超时,应计算剩余时间。重试时从 done 偏移继续,避免重复发送已写入的前缀。
读取同样按实际长度累计,并在读取消息正文前验证长度字段,避免越过接收缓冲区。
错误、重置与关闭
-EAGAIN:在期限内没有传输任何数据。-ECANCELED:操作被k_pipe_reset()中断,或管道正在重置。-EPIPE:管道已经关闭。
k_pipe_reset() 丢弃未读数据、唤醒等待者,并让管道恢复可用的打开状态。
k_pipe_close() 关闭管道并结束等待。多线程应用应统一决定何时重置或关闭,
并让双方重建消息边界;重置后的字节流不能当作旧消息的连续后半段。
当前示例按公共头文件的函数声明编写。官方说明页的个别示意片段可能仍残留旧参数,
集成时应同时核对所用提交的 include/zephyr/kernel.h。