Linux 有名管道阻塞非阻塞
一、文件打开(open)行为分析
当进程针对 FIFO 类型文件发起open调用时,内核根据是否携带O_NONBLOCK标志,表现出不同的同步语义。
1. 默认阻塞模式(未指定O_NONBLOCK)
先打开读端:调用导致进程挂起(进入休眠),直至另一个进程以写方式打开该管道。
先打开写端:调用导致进程挂起,直至另一个进程以读方式打开该管道。
写端曾经打开但已关闭,读操作会读取剩余数据,读完后返回0(不阻塞)。
并发打开:若读端与写端在不同进程中同时打开,双方同时被唤醒,通信链路建立。
单进程死锁风险:同一进程在阻塞模式下,无论是先开读端还是先开写端,单进程都会百分百自锁,则第一次
open将永久阻塞自身,第二次open无法执行,形成典型自死锁场景。
2. 非阻塞模式(指定O_NONBLOCK)
先打开读端:立即成功返回,不受写端是否存在的影响。
先打开写端:立即返回错误,
errno被重置。POSIX 标准强制此行为,旨在贯彻“及早失败”原则——若无读端存在,写端写入的数据将无处可去,应在打开阶段即拒绝,避免后续问题。
二、数据读取(read)行为分析
read() 是否阻塞,完全取决于它操作的那个文件描述符(fd)本身是否被标记为“非阻塞”。所以,设置 read 非阻塞,本质上是设置文件描述符的属性。
| 管道瞬时状态 | 非阻塞模式下的read返回值 | 阻塞模式下的read返回值 | 语义说明 |
|---|---|---|---|
| 所有写端均已关闭 | 0(立即返回) | 0(立即返回) | 通用规则:数据流终结,无论哪种模式,都立即返回 EOF。 |
| 写端存在但缓冲区为空 | -1(errno=EAGAIN) | 进程挂起(休眠),不返回 | 核心分水岭:非阻塞告诉你“暂时没有”;阻塞则直接“睡着等”,直到有数据才醒来。 |
| 写端存在且缓冲区有数据 | 实际字节数 | 实际字节数 | 通用规则:有数据就读,立即返回字节数,模式不影响。 |
| 发生其他系统错误 | -1(其他 errno) | -1(其他 errno) | 通用规则:需根据具体错误码进行异常处理。 |
