我用read_fds中的一个命名pipe道fd调用select。 这个命名pipe道没有写入器,只能以非阻塞,只读模式打开。 我期望select返回与命名pipe道fd标记为准备阅读,并试图从pipe道读取返回0:
从manpage上阅读:
当试图从一个空的pipe道或FIFO读取时:
- 如果没有进程打开pipe道,read()应该返回0来指示文件结束。
但是,只能无限地select块。 这是为什么?
#include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <stdexcept> #include <thread> #include <iostream> int main() { char buf[4096]; // Create a named pipe auto err = mkfifo("/tmp/whatever",0666); if(err) { throw std::runtime_error( std::string("Failed to create fifo ")+ strerror(errno)); } std::thread reader_thread( [&](){ auto fd = open("/tmp/whatever",O_RDONLY|O_NONBLOCK); if(fd < 0) { throw std::runtime_error("Failed to open fifo"); } fd_set fds; while(1) { FD_ZERO(&fds); FD_SET(fd,&fds); std::cerr << "calling select" << std::endl; auto retval = select(fd+1,&fds,nullptr,nullptr,nullptr); if(retval < 0) { std::runtime_error("Failed to call select"); } if(FD_ISSET(fd,&fds)) { auto read_bytes = read(fd,buf,4096); std::cerr << "read " << read_bytes << std::endl; if(read_bytes==0) { break; } } } close(fd); }); reader_thread.join(); return 0; }
从POSIX文档中select
:
当对O_NONBLOCK清除的输入函数的调用不会被阻塞时,描述符应被视为准备好读取,函数是否会成功传输数据。 (该函数可能会返回数据,文件结束指示,或者表示被阻塞的错误以外的错误,并且在这些情况下,描述符应被视为准备好读取。
…
如果所选择的描述符都没有准备好执行所请求的操作,则pselect()或select()函数将被阻塞,直到至少有一个请求的操作准备就绪, 直到发生超时 ,或者直到信号中断为止。
从pipe(7)
联机帮助页面(这是FIFO的基本对象):
如果所有引用管道写入结束的文件描述符都已经关闭 ,那么尝试从管道读取(2)将会看到文件结束(read(2)将返回0)。
介意现在完成时的用法! 这意味着FIFO必须在两侧打开 ,在写入器侧关闭(用于您的应用程序)以生成EOF
状态。
那么,除非fifo最终被作者封闭,为什么要select
return? (fifo-)文件本身的设置是无关紧要的,原因很简单:当使用最有效的方法一次读取多个字节时,会在两侧打开时引入竞争条件。 这是例如命令管道的正常方式:启动读取器进程,然后写入作者(在使用命名管道时通常是完全不相关的程序)。
如果你想select
提前返回,使用timeout
参数。 但通常情况下,一个使用一个单独的线程,可以通过信号来终止(更多信息请参见select
手册页)。
作为旁注:关于Linux / POSIX的一个好处是,使用FIFO还是文件或麦克风驱动程序并不重要。