select()返回无效参数

Rob*_*ert 6 c unix select pipe

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

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

我的想法是,只有在即将准备好读取时才尝试从管道读取,而不是处理任何输入.这似乎有效 - 至少要开始.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);
Run Code Online (Sandbox Code Playgroud)

在循环中:

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);
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Zan*_*ynx 9

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

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

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

它看起来像这样:

struct timeval timeout = {1, 0};
Run Code Online (Sandbox Code Playgroud)

此外,在您的选择循环中,您应该知道Linux会将剩余时间写入超时值.这意味着它会不会是通过循环下一次1秒,除非你将该值重置为1秒.