为什么 select 在没有编写器的命名管道上会无限期地阻塞?
Why does select on a named pipe with no writers block indefinitely?
我在 read_fds 中使用单个命名管道 fd 调用 select。此命名管道没有编写器,仅以非阻塞、只读模式打开。我希望带有命名管道 fd 的 select returns 标记为准备好读取,并且试图从管道 returns 0:
读取
从联机帮助页阅读:
When attempting to read from an empty pipe or FIFO:
- If no process has the pipe open for writing, read() shall return 0 to > indicate end-of-file.
然而,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;
}
来自 select
的 POSIX 文档:
A descriptor shall be considered ready for reading when a call to an input function with O_NONBLOCK clear would not block, whether or not the function would transfer data successfully. (The function might return data, an end-of-file indication, or an error other than one indicating that it is blocked, and in each of these cases the descriptor shall be considered ready for reading.
...
If none of the selected descriptors are ready for the requested operation, the pselect() or select() function shall block until at least one of the requested operations becomes ready, until the timeout occurs, or until interrupted by a signal.
来自 pipe(7)
联机帮助页(这是 FIFO 的基础对象):
If all file descriptors referring to the write end of a pipe have been closed, then an attempt to read(2) from the pipe will see end-of-file (read(2) will return 0).
注意现在完成时的用法!这意味着 FIFO 必须在两侧打开 首先 ,在写入端(对于您的应用程序)关闭以生成 EOF
条件。
所以,除非 fifo 最终被作者关闭,否则为什么要 select
return? (fifo-) 文件本身的设置是无关紧要的:当使用最有效的方法一次读取多个字节时,它会在打开两侧之间引入竞争条件。这是例如的正常方式一个命令管道:启动 reader 进程并稍后运行编写器(在使用命名管道时通常是一个完全不相关的程序)。
如果您希望 select
提前到 return,请使用 timeout
参数。但通常情况下,一个人使用一个可以被信号终止的单独线程(有关更多信息,请参见 select
手册页)。
旁注:Linux/POSIX 的一个好处是,无论您使用 FIFO、文件还是麦克风驱动程序,都没有关系。
我在 read_fds 中使用单个命名管道 fd 调用 select。此命名管道没有编写器,仅以非阻塞、只读模式打开。我希望带有命名管道 fd 的 select returns 标记为准备好读取,并且试图从管道 returns 0:
读取从联机帮助页阅读:
When attempting to read from an empty pipe or FIFO:
- If no process has the pipe open for writing, read() shall return 0 to > indicate end-of-file.
然而,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;
}
来自 select
的 POSIX 文档:
A descriptor shall be considered ready for reading when a call to an input function with O_NONBLOCK clear would not block, whether or not the function would transfer data successfully. (The function might return data, an end-of-file indication, or an error other than one indicating that it is blocked, and in each of these cases the descriptor shall be considered ready for reading.
...
If none of the selected descriptors are ready for the requested operation, the pselect() or select() function shall block until at least one of the requested operations becomes ready, until the timeout occurs, or until interrupted by a signal.
来自 pipe(7)
联机帮助页(这是 FIFO 的基础对象):
If all file descriptors referring to the write end of a pipe have been closed, then an attempt to read(2) from the pipe will see end-of-file (read(2) will return 0).
注意现在完成时的用法!这意味着 FIFO 必须在两侧打开 首先 ,在写入端(对于您的应用程序)关闭以生成 EOF
条件。
所以,除非 fifo 最终被作者关闭,否则为什么要 select
return? (fifo-) 文件本身的设置是无关紧要的:当使用最有效的方法一次读取多个字节时,它会在打开两侧之间引入竞争条件。这是例如的正常方式一个命令管道:启动 reader 进程并稍后运行编写器(在使用命名管道时通常是一个完全不相关的程序)。
如果您希望 select
提前到 return,请使用 timeout
参数。但通常情况下,一个人使用一个可以被信号终止的单独线程(有关更多信息,请参见 select
手册页)。
旁注:Linux/POSIX 的一个好处是,无论您使用 FIFO、文件还是麦克风驱动程序,都没有关系。