2014-03-29 52 views
1

我在Mac OS 10.9上看到select()和poll()這兩個行爲都是我無法解釋的。幫助我理解我可能是做錯了,或者這可能是一個OS錯誤(很難相信...)select()和poll()在Mac OS上缺少一個關閉的管道

我在幹什麼

我的程序監視文件描述符成爲可讀在後臺線程中使用select()或poll()。這個bug在兩個實現中都是一樣的,所以我只描述一下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); 

讓我們來看看它: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) 

事件字段的值對應於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); 
} 

雖然在民意調查()還是停了下來,我從調試器中運行這個:

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

如果讀取返回0,這是poll應該捕獲的文件結束條件。

Futher證據

如果我給poll()將超時,並在一個循環中運行它,就像這樣:

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

然後問題消失了,我可以看到民意調查()塊,然後找到EOF的文件描述符,但永遠不會超過1秒的延遲。推測poll()在文件描述符在被調用之前已經關閉時起作用,但當它已經在poll()時關閉時會產生困惑。

還有什麼可能繼續?

這是一個複雜的程序,我不能在一個普通的小例子中再現這個問題。因此,在主線程上可能會發生混淆問題的情況。我希望有人能以這種方式建議可能干擾民意調查的事情()。從主線中刪除代碼會使問題發生得更少,因此在找到吸菸槍之前我無法解決問題。

來源 如果你真的很好奇,源代碼可以在這裏找到(注意分支是「pollHelper」): https://github.com/gnachman/iTerm2/tree/pollHelper

複製,打開一個新的終端,並密切與控制 - D.它發生在我20-30%的時間裏。

在後臺運行下面的程序使得它在我2013 13" 的MacBook Pro更經常發生:

int main() { while (1); return 0; } 
+0

使用'errno'用'perror'當'poll'失敗 –

+0

目前還不清楚什麼順序如果事情。如果read()返回0,那麼在讀取()返回0後,如果您再次觀看文件描述符,則不能保證輪詢/選擇將發出任何信號。 – nos

+0

您是否在輪詢之前將文件描述符設置爲非阻塞/選擇 ? – wildplasser

回答

-1

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

如果讀返回0,這是最終OF-文件條件,民意調查應該已經捕獲。

你確定嗎?errno = 35說EAGAIN - 對我來說,不廁所k像EOF條件。

(張貼的答案,因爲似乎引號不要在註釋中支持?)

+0

Errno僅在read()返回-1時有效。其他情況下,它包含一些隨機(以前)的值。 – wildplasser

+0

這是 - 當然 - 真的。快速拍攝有時會錯過,對不起。 – mfro