Android随笔-pipe是什么?

pipe 是 Linux 内核提供的进程间通信(IPC)机制 ,本质上是一个内核管理的环形缓冲区(ring buffer) ,让两个进程可以通过文件描述符(FD)单向传输字节流。

一、pipe 是什么?

特性 说明
本质 内核中的环形缓冲区,存在于 RAM 中
方向 单向(半双工):数据只能从一个方向流动
顺序 FIFO(先进先出)
生命周期 随进程结束而销毁,不持久化到磁盘
创建 pipe() 系统调用创建 2 个 FD

二、核心 API

c 复制代码
#include <unistd.h>

int pipe(int pipefd[2]);
// pipefd[0] = 读端(read end)
// pipefd[1] = 写端(write end)

// 现代版本:
int pipe2(int pipefd[2], int flags);
// flags: O_NONBLOCK(非阻塞), O_CLOEXEC(执行时关闭)

// 示例:
int fd[2];
pipe(fd);  // 或 pipe2(fd, O_NONBLOCK | O_CLOEXEC)

write(fd[1], "Hello", 5);   // 向写端写入
read(fd[0], buf, 100);       // 从读端读取

三、架构图

四、内核实现:环形缓冲区

c 复制代码
// Linux Kernel: fs/pipe.c

struct pipe_inode_info {
    struct mutex mutex;
    struct pipe_buffer *bufs;      // 环形缓冲区数组
    unsigned int head;              // 写指针
    unsigned int tail;              // 读指针
    unsigned int ring_size;         // 缓冲区大小(默认 16 页 = 64KB)
    struct page *tmp_page;
    struct fasync_struct *fasync_readers;
    struct fasync_struct *fasync_writers;
    struct inode *inode;
    struct user_struct *user;
};

关键点:

  • 内核分配一个页(page)数组作为缓冲区
  • 默认大小:16 页 = 64KB(PIPE_BUF)
  • 写指针和读指针维护 FIFO 顺序
  • 缓冲区满时,write() 阻塞(默认)或返回 EAGAIN(非阻塞)

五、阻塞 vs 非阻塞行为

场景 阻塞模式(默认) 非阻塞模式(O_NONBLOCK
read() 时缓冲区为空 阻塞,直到有数据 返回 -1errno = EAGAIN
write() 时缓冲区满 阻塞,直到有空间 返回 -1errno = EAGAIN
write() < PIPE_BUF 原子写入(全有或全无) 原子写入
write() > PIPE_BUF 可能拆分写入 可能拆分写入

六、pipe 在 Android Looper 中的应用(Legacy)

旧版 Android(Android 5.0 之前)用 pipe 实现 Looper 的线程唤醒:

cpp 复制代码
// system/core/libutils/Looper.cpp (Legacy)

Looper::Looper(bool allowNonCallbacks) {
    // 创建 pipe
    int wakeFds[2];
    pipe(wakeFds);  // or pipe2(wakeFds, O_CLOEXEC)
    
    mWakeReadPipeFd = wakeFds[0];   // 读端
    mWakeWritePipeFd = wakeFds[1];   // 写端
    
    // 将读端注册到 epoll
    struct epoll_event eventItem;
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeReadPipeFd;
    epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
}

void Looper::wake() {
    // 向写端写入 1 字节,触发 epoll_wait 返回
    write(mWakeWritePipeFd, "W", 1);
}

void Looper::awoken() {
    // 从读端读取,清空 pipe
    char buffer[16];
    read(mWakeReadPipeFd, buffer, sizeof(buffer));
}

七、为什么 Android 从 pipe 切换到 eventfd?

对比项 pipe eventfd
FD 数量 2(读+写) 1
内核缓冲区 2 个 ring buffer 1 个 64 位计数器
原子性 不保证 保证 64 位原子操作
系统调用开销 pipe() + 2 次 fcntl() eventfd() 一次
适用场景 数据传输 简单通知
Android 使用 Legacy(pre-5.0) Modern(5.0+)

eventfd 更适合"通知"场景,因为它就是设计来做轻量级事件通知的。

八、常见使用场景

场景 说明
Shell 管道 `ls grep txt---ls的 stdout 通过 pipe 连接到grep` 的 stdin
父子进程通信 fork() 后,pipe() 让父子进程交换数据
Android Looper Legacy 唤醒机制(已被 eventfd 替代)
自管道技巧 信号处理中,信号处理函数向 pipe 写,主循环从 pipe 读,避免信号处理中的异步问题

九、总结

pipe 是 Linux 内核提供的单向字节流 IPC 机制,通过 pipe() 创建两个文件描述符(fd0 读端、fd1 写端),数据在内核环形缓冲区中按 FIFO 顺序流动。旧版 Android Looper 用 pipe 实现线程唤醒(写端触发 epoll_wait 返回),现代 Android 用更高效的 eventfd 替代了 pipe。

相关推荐
不会就选b10 小时前
Linux之文件--fd,重定向
linux
三十岁老牛再出发10 小时前
07.26每日总结
linux·c语言·mysql
fengyehongWorld10 小时前
Linux update-alternatives命令
linux
雨的旋律209911 小时前
ubuntu2604
linux·运维·服务器
晓梦林11 小时前
[ACTF2020 新生赛]BackupFile学习笔记
android·笔记·学习
三言老师11 小时前
mkdir创建多级目录,目录创建基础实操
linux·运维·服务器
ITmaster073111 小时前
告别 IDE?Android CLI 来了,开发进入 AI Agent 时代
android·ide·人工智能
imc.1112 小时前
linux EXT文件系统
linux·运维·服务器
aaajj12 小时前
【Android】获取设备音频
android
三言老师13 小时前
mv移动文件、重命名文件实战案例
linux·服务器·网络·centos