2009-02-12 54 views
24

select(2)函數在其正在監視的文件描述符被另一個線程關閉時的行爲是什麼?如果關閉(2)單獨線程中的文件描述符,select(2)會做什麼?

從一些粗略的測試,它並立即返回。我懷疑結果是(a)它仍然在等待數據,但如果你真的試圖讀取它,你會得到EBADF(可能 - 有潛在的種族)或(b)它假裝像文件描述符從來沒有被傳入。如果後一種情況是真的,傳遞一個沒有超時的fd會導致死鎖,如果它被關閉。

+0

[從套接字選擇突破]的可能重複(http://stackoverflow.com/questions/2486727/breaking-out-from-socket-select) – iammilind 2016-05-04 13:22:41

+0

我認爲它是不同的,雖然稍有關係。另一個問題是明確地詢問如何從另一個線程中拋出一個`select()`(而`pipe()`是個很好的答案),而我的更多的是關於`select()`的行爲, )`ed socket。在下面的答案中,你會看到答案是,「這取決於」。 – 2016-05-10 15:15:45

+0

我繼續發現的一個更神祕的bug追捕原來只是由於這個問題:線程A在socket #x上選擇了線程B關閉。此後不久,線程C創建了一個新的套接字,這也恰好是套接字#x(因爲網絡堆棧選擇重新使用新套接字的數字x)。此時,線程A(仍然嘗試使用套接字#x)開始在線程C的套接字上選擇/讀取/寫入數據,儘管它們之間完全沒有邏輯連接。這是一個追查的完全痛苦。 – 2017-01-20 20:53:13

回答

21

從一些額外的調查,似乎都DWC和bothie是正確的。

bothie's answer問題歸結爲:它是未定義的行爲。這並不意味着它是不可預測的,但不同的操作系統會以不同的方式進行操作。在這種情況下,似乎Solaris和HP-UX系統從select(2)返回,但Linux不基於2001年的this post to the linux-kernel mailing list.

linux-kernel郵件列表上的參數基本上是未定義的(並且已損壞)行爲依賴。在Linux的情況下,在文件描述符上調用close(2)會有效地減少引用計數。由於有一個select(2)調用也參考它,該fd將保持打開並等待輸入,直到select(2)返回。這基本上是dwc's answer。你將在文件描述符上得到一個事件,然後它將被關閉。試圖讀取它將導致一個EBADF,假設fd沒有被回收。 (關注MarkR在his answer中所做的,儘管我認爲在大多數情況下可以通過適當的同步來避免)

所以,非常感謝大家的幫助。

6

我希望它會表現得好像檔案結尾已經達成,這就是說,它會以顯示爲準備文件描述符,但任何嘗試讀取它隨後將返回「錯誤的文件描述符返回」。

話雖如此,這樣做是非常不好的做法,反正你總是有潛在的競爭條件與相同數量的另一文件描述符可以通過另一個線程後,立即另2日關閉了它打開,那麼選擇線程將最終等待錯誤的線程。

只要你關閉一個文件,它的號碼變成可再利用,並可以通過下一次調用得到重用開(),插座()等,即使被另一個線程。因此,你真的需要避免這種事情。

+0

我以爲它可能會隨時準備就緒,但這並不完全正確:描述符實際上並未處於就緒狀態 - 它已關閉。正如你所提到的,到你使用它的時候,它可能會被重新分配給別的東西。 – 2009-02-12 22:07:30

2

這是一個有點混亂,你問...

選擇()在一個「有趣」的變化應該返回。如果close()僅僅減少了引用計數並且文件仍然處於打開狀態以便寫入某處,那麼沒有理由使select()喚醒。

如果其他線程上唯一的開放描述那樣接近(),然後它變得更有趣,但我需要看到代碼的簡單版本,看看是否有什麼地方真的錯了。

5

select系統調用是一種等待文件desctriptors改變狀態,而程序沒有任何其他的事情。主要用於服務器應用程序,這些應用程序打開一堆文件描述符,然後等待它們執行任何操作(接受新連接,讀取請求或發送響應)。這些文件描述符將以非阻塞io模式打開,以便服務器進程在任何時候都不會掛在系統調用中。

這也意味着,不需要單獨的線程,因爲所有可以在線程中完成的工作都可以在選擇調用之前完成。如果工作需要很長時間,而不是可以中斷,選擇使用timeout = {0,0}調用,文件描述符被處理,之後工作正在恢復。

現在,你在另一個線程關閉文件描述符。爲什麼你有這個額外的線程,爲什麼它會關閉文件描述符?

POSIX標準沒有提供任何提示,在這種情況下會發生什麼,所以你在做什麼是未定義的行爲。預計在不同的操作系統之間,甚至是同一操作系統的版本之間,結果會有很大的不同。

問候,博多