select() 返回无效参数

问题描述 投票:0回答:4

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

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

我的想法是仅在管道即将准备好读取时才尝试从管道中读取数据,而不是处理任何输入。这似乎是有效的——至少在开始时是这样。 select() 设置 fd_set,如果 FD_ISSET,我会从 FD 读取 1 个字节。但是 select() 说“是”的次数太多了,并且 read() 会阻塞。

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

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

我可以根据要求发布代码,但这是沼泽标准。设置一个 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(interp_output[1], "w") 打开的 FILE* 上的 fprintf - 这是在不同的线程中。我所要做的就是我的“提示>” - 它正确地打印了所有内容,但又进行了一次不应该进行的迭代。我关闭了缓冲,这给我带来了其他问题。

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

编辑4:所以现在我完全困惑了。不知何故,.tv_sec=1 的超时值不好。通过摆脱它,代码就可以正常工作了。如果有人有任何理论,我会洗耳恭听。我会将其保留为 NULL,除非该线程需要定期进行更新。

c unix pipe posix-select
4个回答
10
投票

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

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

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

它看起来像这样:

struct timeval timeout = {1, 0};

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


3
投票

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

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

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

顺便说一下,在标准/系统/通用函数之后命名变量是一个非常糟糕的主意。 “read_fds”将是一个比“read”更好的名字。


2
投票
http://linux.die.net/man/2/select

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

唯一的解决方案是使用非阻塞套接字。


0
投票

我多次遇到类似的情况,通常结果是在其他地方存在愚蠢的拼写错误或剪切和粘贴错误,导致了我误诊的行为。

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

还要考虑使用异步 IO,即使只是为了调试目的。如果您怀疑正在发生的事情确实发生了,那么 read() 将返回 EWOULDBLOCK。

另外,你说你正在复制fd_set。如何?你能发布你的代码吗?

© www.soinside.com 2019 - 2024. All rights reserved.