Android随笔-pipe是什么?

📅 2026/7/6 23:43:18 👁️ 阅读次数 📝 编程学习
Android随笔-pipe是什么?

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

一、pipe 是什么?

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

二、核心 API

#include<unistd.h>intpipe(intpipefd[2]);// pipefd[0] = 读端(read end)// pipefd[1] = 写端(write end)// 现代版本:intpipe2(intpipefd[2],intflags);// flags: O_NONBLOCK(非阻塞), O_CLOEXEC(执行时关闭)// 示例:intfd[2];pipe(fd);// 或 pipe2(fd, O_NONBLOCK | O_CLOEXEC)write(fd[1],"Hello",5);// 向写端写入read(fd[0],buf,100);// 从读端读取

三、架构图

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

// Linux Kernel: fs/pipe.cstructpipe_inode_info{structmutexmutex;structpipe_buffer*bufs;// 环形缓冲区数组unsignedinthead;// 写指针unsignedinttail;// 读指针unsignedintring_size;// 缓冲区大小(默认 16 页 = 64KB)structpage*tmp_page;structfasync_struct*fasync_readers;structfasync_struct*fasync_writers;structinode*inode;structuser_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 的线程唤醒:

// system/core/libutils/Looper.cpp (Legacy)Looper::Looper(boolallowNonCallbacks){// 创建 pipeintwakeFds[2];pipe(wakeFds);// or pipe2(wakeFds, O_CLOEXEC)mWakeReadPipeFd=wakeFds[0];// 读端mWakeWritePipeFd=wakeFds[1];// 写端// 将读端注册到 epollstructepoll_eventeventItem;eventItem.events=EPOLLIN;eventItem.data.fd=mWakeReadPipeFd;epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);}voidLooper::wake(){// 向写端写入 1 字节,触发 epoll_wait 返回write(mWakeWritePipeFd,"W",1);}voidLooper::awoken(){// 从读端读取,清空 pipecharbuffer[16];read(mWakeReadPipeFd,buffer,sizeof(buffer));}

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

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

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

八、常见使用场景

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

九、总结

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