我成功地从另一个线程的管道中读取数据,并打印输出(恰好在 ncurses 窗口中)。
出于各种原因,我需要一次执行一个字符,并且我在 FD 上使用 select() 作为管道的读取端,以及一些其他 FD(如标准输入)。
我的想法是仅在管道即将被读取时才尝试从管道读取,而不是处理任何输入。这似乎在起作用——至少开始了。 select() 设置 fd_set,如果是 FD_ISSET,我会从 FD 读取 1 个字节。但是 select() 说是一次太多了,read() 阻塞了。
所以我的问题是 - 为什么 select() 会报告 fd 已准备好读取,如果后续的 read() 阻塞?
(大约)当管道的另一端连接到一个 fork 进程时,同样的代码可以正常工作,如果有帮助的话。
我可以根据要求发布代码,但它是沼泽标准。设置一个 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() 有什么问题?我更新了代码并更改了标题以更准确地反射(reflect)问题...
编辑 4:所以现在我完全糊涂了。不知何故,.tv_sec=1 的超时值不好。通过摆脱它,代码工作得很好。如果有人有任何理论,我洗耳恭听。我会将其保留为 NULL,除非该线程需要定期进行更新。
最佳答案
为了绝对保证读取永远不会阻塞,您必须在 fd 上设置 O_NONBLOCK。
您的选择错误几乎可以肯定是因为您没有设置整个时间结构。你只是在设置秒数。另一个字段将包含从堆栈中拾取的垃圾数据。
使用结构初始化。这将保证其他字段设置为 0。
看起来像这样:
struct timeval timeout = {1, 0};
此外,在您的选择循环中,您应该知道 Linux 会将剩余时间写入超时值。这意味着下一次循环不会为 1 秒,除非您将该值重置为 1 秒。
关于c - select() 返回无效参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4364474/