为何第二次调用的select()会陷入阻塞?
问题原因与修复方案
核心原因
select()调用会直接修改传入的fd_set集合,仅保留触发了事件的文件描述符。第一次select()因stdin输入触发返回后,set集合中只剩下stdin的文件描述符(0),管道读端的fd已被移除,导致第二次select()根本没有监听管道读端,自然不会因管道有数据而返回。
另外,代码中第二次select()后的read操作存在错误:误读了stdin而非管道读端。
修复步骤
- 每次调用select()前必须重新初始化fd_set:
- 调用
FD_ZERO()清空集合 - 重新添加所有需要监听的文件描述符
- 调用
- 修正read操作的目标文件描述符:从管道读端
fd_pipe[0]读取数据,而非stdin。
修复后的代码
int fd_pipe[2]; char buf[20]; fd_set set; pipe(fd_pipe); // 第一次select准备 FD_ZERO(&set); FD_SET(fd_pipe[0], &set); FD_SET(0, &set); select(fd_pipe[0]+1, &set, NULL, NULL, NULL); if (FD_ISSET(0, &set)) { read(0, buf, 20); write(fd_pipe[1], buf, 1); } // 第二次select前重新初始化集合 FD_ZERO(&set); FD_SET(fd_pipe[0], &set); // 若无需再监听stdin,可移除FD_SET(0, &set) FD_SET(0, &set); select(fd_pipe[0]+1, &set, NULL, NULL, NULL); if (FD_ISSET(fd_pipe[0], &set)) { char d; read(fd_pipe[0], &d, 1); // 修正为读取管道读端 assert(d == buf[0]); }
多线程场景补充
- 确保管道文件描述符的操作线程安全,避免写入和select监听的竞态问题
- 若仅需通过管道触发取消操作,第二次select可只监听管道读端,减少不必要的资源消耗
内容的提问来源于stack exchange,提问作者Stewart
相关产品推荐
相关产品推荐

