我在MacOS10.9上看到select()和poll()的行为,我无法解释。帮助我理解我可能做错了什么,或者这可能是一个操作系统错误(难以置信…)
我在做什么
我的程序使用select()或poll()监视文件描述符在后台线程中的可读性。这两个实现的bug都是一样的,所以我将只描述poll的情况。
有问题的文件描述符是用forkpty()创建的,因此它是伪终端的一端。我调用execvp()在子进程中运行一个shell,最终它终止。这将导致父级中的文件描述符在读取所有缓冲输出(即,读取返回0)后,从read()生成文件结束结果。
正常的操作过程是在这个文件描述符上交替调用poll()和read()。最终read()返回0,我可以清理,知道子进程已经完成。
怎么了
有一点出乎意料:有时当子进程完成并且文件描述符处于EOF状态时,poll()将不会返回。
为什么我认为poll()行为不端
下面是在lldb中演示的一个会话。子进程完成后,我在poll()中阻塞时停止了它。

(lldb) bt
* thread #11: tid = 0x24ee79, 0x00007fff904a594a libsystem_kernel.dylib`poll + 10
    frame #0: 0x00007fff904a594a libsystem_kernel.dylib`poll + 10
  * frame #1: 0x00000001001f2672 iTerm`-[iTermPollHelper poll](self=0x000060800042db00, _cmd=0x00007fff9349800b) + 194 at iTermPollHelper.m:117
    frame #2: 0x000000010014a87b iTerm`-[TaskNotifier run](self=0x000060000045c620, _cmd=0x00007fff8fda8066) + 4251 at TaskNotifier.m:216
    frame #3: 0x00007fff8c86c76b Foundation`__NSThread__main__ + 1318
    frame #4: 0x00007fff934a8899 libsystem_pthread.dylib`_pthread_body + 138
    frame #5: 0x00007fff934a872a libsystem_pthread.dylib`_pthread_start + 137

好,所以线程11在poll()中阻塞。以下是我对民意测验的要求:
numDescriptors = poll(pollfds, count, -1);

让我们检查一下:
(lldb) p count
(int) $2 = 2
(lldb) p pollfds[0]
(pollfd) $3 = (fd = 6, events = 1, revents = 0)
(lldb) p pollfds[1]
(pollfd) $4 = (fd = 5, events = 1, revents = 0)

事件字段的值1对应于POLLIN。在本例中,fd 5是感兴趣的。我们已经证明poll()正在监视文件描述符5,如果它处于EOF状态,poll现在应该已经返回。我可以做到:
(lldb) finish

poll()不会返回。所以它肯定被屏蔽了,必须相信在fd 5上没有什么可以阅读的。
我的程序包括以下功能:
void TryReadingFromFd(int fd) {
    char buffer[1];
    int n = read(fd, buffer, 1);
    NSLog(@"Read returns %d, errno=%d", n, errno);
}

在poll()中仍然停止时,我从调试器运行此命令:
(lldb) expr (void)TryReadingFromFd(5)
2014-03-26 21:53:43.684 iTerm[48604:af07] Read returns 0, errno=35

如果read返回0,则这是轮询应该捕获的文件结束条件。
进一步的证据
如果我给poll()一个超时并在循环中运行它,如下所示:
do {
  numDescriptors = poll(pollfds, count, 1000);
} while (numDescriptors == 0);

然后问题消失了,我可以看到poll()块,然后找到EOF'ed文件描述符,但决不能超过1秒的延迟。可能poll()在文件描述符在调用之前已经关闭时工作,但在poll()中关闭时会混淆。
还可能发生什么事?
这是一个复杂的程序,我不能在一个微不足道的小例子中重现这个问题。所以在主线上可能会发生一些事情来混淆这个问题。我希望有人能提出什么会干扰poll()的建议。从主线程中删除代码会使问题发生的频率降低,因此在找到冒烟的枪之前,我无法将问题解决。
来源
如果您真的很好奇,可以在这里找到源代码(注意分支是“pollHelper”):
https://github.com/gnachman/iTerm2/tree/pollHelper
为了复制,打开一个新的终端,然后用Control-D关闭。我大约有20-30%的时间会这样。
在后台运行以下程序会使我的2013 13“Macbook Pro更频繁地运行:
int main() { while (1); return 0; }

最佳答案

(lldb) expr (void)TryReadingFromFd(5) 2014-03-26 21:53:43.684 iTerm[48604:af07] Read returns 0, errno=35
如果read返回0,则这是轮询应该捕获的文件结束条件。
你确定吗?errno=35表示EAGAIN-对我来说,这看起来不像是EOF条件。
(由于评论中似乎不支持引号,所以作为答案发布?)

关于c - select()和poll()在Mac OS上缺少封闭的管道,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22734358/

10-12 18:39