select()和poll()在Mac OS上缺少一个已关闭的管道

时间:2014-03-29 17:29:00

标签: c macos unix select file-descriptor

我在Mac OS 10.9上看到select()和poll()的行为,我无法解释。帮助我理解我可能做错了什么,或者这可能是操作系统错误(很难相信......)

我在做什么

我的程序使用select()或poll()监视文件描述符在后台线程中可读。该错误与任一实现相同,因此我将描述使用poll进行的操作。

有问题的文件描述符是用forkpty()创建的,所以它是伪终端的一端。我调用execvp()在子进程中运行shell,最终终止。这会导致父级中的文件描述符在读取所有缓冲输出(即读取返回0)后从read()生成文件结束结果。

正常的操作过程是通过对此文件描述符的read()调用来交替调用poll()。最终read()返回0,我可以清理,知道子进程已经完成。

什么错误

这是意外的:当子进程完成且文件描述符处于EOF状态时,有时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状态,则轮询应该已经返回。我可以这样做:

(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应该捕获的文件结束条件。

进一步的证据

如果我给poll()一个超时并在循环中运行它,就像这样:

do {
  numDescriptors = poll(pollfds, count, 1000);
} while (numDescriptors == 0);

然后问题就消失了,我可以看到poll()块然后找到EOF的文件描述符,但是从不超过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; }

1 个答案:

答案 0 :(得分:-1)

  

(lldb) expr (void)TryReadingFromFd(5) 2014-03-26 21:53:43.684 iTerm[48604:af07] Read returns 0, errno=35

     

如果read返回0,那就是poll应该捕获的文件结束条件。

你确定吗? errno = 35说EAGAIN - 对我来说看起来不像EOF条件。

(作为答案发布,因为评论似乎不支持引号?)