select()返回无效参数

时间:2010-12-06 08:27:19

标签: c unix select pipe

我正在从另一个线程的管道中成功读取并打印输出(在ncurses窗口中发生)。

出于各种原因,我需要一次完成一个字符,并且我在FD上使用select()作为管道的读取端,以及一些其他FD(如stdin)。 / p>

我的想法是,只有在即将准备好读取时才尝试从管道中读取,而不是处理任何输入。这似乎有效 - 至少要开始。 select()设置fd_set,如果FD_ISSET,我从FD执行1字节的read()。但是select()说是一次太多了,而read()阻塞了。

所以我的问题是这个 - 为什么select()报告fd已准备好读取,如果后续的read()阻塞?

(大约)当管道的另一端连接到分叉进程时,同样的代码工作正常,如果这有帮助。

我可以根据要求发布代码,但它是标准的bog。设置一个fd_set,复制它,选择副本,如果FD被设置调用一个从同一个FD读取一个字节的函数...否则还原fd_set副本

编辑:根据要求,这是代码:

设置我的fd_set:

fd_set fds;
FD_ZERO(&fds); 
FD_SET(interp_output[0], &fds);
FD_SET(STDIN_FILENO, &fds);
struct timeval timeout, tvcopy; timeout.tv_sec=1;
int maxfd=interp_output[0]+1; //always >stdin+1
fd_set read_fds;
FD_COPY(&fds, &read_fds);

循环:

if (select(maxfd, &read_fds, NULL, NULL, &timeout)==-1) {perror("couldn't select"); return;}
if (FD_ISSET(interp_output[0], &read_fds)) {
    handle_interp_out();
} else if (FD_ISSET(STDIN_FILENO, &read_fds)) {
//waddstr(cmdwin, "stdin!"); wrefresh(cmdwin);
    handle_input();
}

FDCOPY(&fds, &read_fds);

handle_interp_out():

void handle_interp_out() {
    int ch;
    read(interp_output[0], &ch, 1);
    if (ch>0) {
            if (ch=='\n') { if (cmd_curline>=cmdheight) cmdscroll(); wmove(cmdwin, ++cmd_curline, 1); }
            else waddch(cmdwin, ch);
            wrefresh(cmdwin);
    }
}

编辑2:写代码只是用fdopen打开的FILE *上的一个fprintf(interp_output [1],“w”) - 这是一个不同的线程。我所要做的只是我的“提示>” - 它打印所有正确的,但再做一次它不应该的迭代。我已经关闭了缓冲,这给了我其他问题。

编辑3:这已经成为我调用select()的问题。看起来,它立即返回-1并且errno被设置为'无效参数'。 read()不知道这一点,只是继续前进。我的select()有什么问题?我已更新代码并更改了标题以更准确地反映问题...

编辑4:所以现在我完全糊涂了。不知何故,.tv_sec = 1的超时值并不好。通过摆脱它,代码工作得很好。如果有人有任何理论,我都会听到。我只想把它留在NULL,除了这个线程需要定期进行更新。

4 个答案:

答案 0 :(得分:8)

为了绝对保证读取永远不会阻止,你必须在fd上设置O_NONBLOCK。

您的选择错误几乎肯定是因为您没有设置整个时间结构。你只设置秒。另一个字段将包含从堆栈中拾取的垃圾数据。

使用struct初始化。这将保证其他字段设置为0。

看起来像这样:

struct timeval timeout = {1, 0};

此外,在您的select循环中,您应该知道Linux会将剩余时间写入超时值。这意味着,除非您将值重置为1秒,否则下次循环时将为1秒。

答案 1 :(得分:3)

根据联系方式:

出错时,返回-1,并正确设置errno;集合和超时变得未定义,因此在发生错误后不要依赖它们的内容。

您没有从select()中检查返回码。

最可能的解释是select()正在被中断(errno = EINTR),因此返回错误,并且FD位仍然在“read”fd_set中设置,为您提供您所看到的行为。

顺便提一下,在标准/系统/通用功能之后命名变量是一个非常糟糕的主意。 “read_fds”比“阅读”更有名。

答案 2 :(得分:2)

这是对的。请参阅Linux中的select()联机帮助页,例如:http://linux.die.net/man/2/select

“在Linux下,select()可能会将套接字文件描述符报告为”准备好读取“,而后续的读取块”

唯一的解决方案是使用NON-BLOCKING套接字。

答案 3 :(得分:0)

答案是“它不会”。您描述的行为永远不会发生。还有别的东西出了问题。

我多次遇到过类似的情况,通常结果发现其他地方有一个愚蠢的错字或剪切和粘贴错误导致我误诊的行为。

如果你发布你的代码,也许我们可以提供更好的帮助 - 发布写代码以及阅读。

另外考虑使用异步IO,即使仅用于调试目的。如果您怀疑发生的事情真的发生了,那么read()将返回EWOULDBLOCK。

另外,你说你正在复制fd_set。怎么样?你可以发布你的代码吗?