我在使用管道
时被
select()
堵塞。当我们在标准输入上获取数据时,第一个 select()
将解锁(我只需输入一个字母并按 Enter)。
然后我将数据写入管道的写入端,但是
select()
无法识别管道读取fd上的任何数据。所以它会无限期地阻塞。
int fd_pipe[2];
char buf[20];
fd_set set;
pipe(fd_pipe);
FD_ZERO(&set);
FD_SET(fd_pipe[0], &set); // Pipe read fd
FD_SET(0 , &set); // stdin fd
select(fd_pipe[0]+1, &set, NULL, NULL, NULL); // I type 1 char + ENTER to get past this point
if (FD_ISSET(0, &set))
{
read(0, buf, 20); // Can confirm we do get here
write(fd_pipe[1], buf, 1); // Lets put just one character in the pipe
}
select(fd_pipe[0]+1, &set, NULL, NULL, NULL); // <-- We get stuck here
if (FD_ISSET(fd_pipe[0], &set))
{
char d;
read(0, &d, 1);
assert(d == buf[0]);
}
如果我在没有
select()
的情况下执行此操作,则一切正常:
int fd_pipe[2];
pipe(fd_pipe);
char c = 'x';
write( fd_pipe[1], &c, 1);
c = 'a';
read( fd_pipe[0], &c, 1);
assert(c == 'x');
在更大的图中,我有一个多线程程序,它使用
select()
和 pipe()
来模拟取消 read()
操作。我用单个字符写入管道,目的是强制 select()
返回,以便我可以关闭操作,而不是尝试发送信号来取消主 FD 上的阻塞 read()
。但select()
没有回来。
select
函数会修改您传递给它的集合。当 select
返回时,集合将仅包含活动描述符。
在您的情况下,只会设置
STDIN_FILENO
,因此第二次调用 select
时不会有 fd_pipe[0]
。
解决方案实际上不是将管道重新添加到集合中并再次调用
select
,而是只调用 select
一次:
FD_ZERO(&set);
FD_SET(fd_pipe[0], &set); // Pipe read fd
FD_SET(0 , &set); // stdin fd
select(fd_pipe[0]+1, &set, NULL, NULL, NULL); // I type 1 char + ENTER to get past this point
if (FD_ISSET(0, &set))
{
read(0, buf, 20); // Can confirm we do get here
write(fd_pipe[1], buf, 1); // Lets put just one character in the pipe
}
else if (FD_ISSET(fd_pipe[0], &set))
{
char d;
read(fd_pipe[0], &d, 1);
assert(d == buf[0]);
}
您还需要检查
select
实际返回什么。它可能会返回 -1
,这意味着存在错误。
如果您确实需要多次致电
select
。然后您需要重新调零/重新设置 fd_set
。