1. 程式人生 > >Windows,Linux的select函式功能差異

Windows,Linux的select函式功能差異

Windows,Linux的select函式功能差異

感謝主,Windows當年也實現了select函式,這讓我們的跨平臺大業至少順暢了一節。但由於Windows滲入骨髓的叛逆心理,他總要和UNIX的實現保持一些差別,讓你無可奈何。首先是Windows的select函式的引數介面設計和Linux下有較大差別,這個在我的《設計極其糟糕的select函式》就討論過,相對而言,在引數設計上,Windows的設計明顯好於Linux。這次我們聊聊他們的功能差異。

1          無控制代碼等待觸發時的處理的差異

最近的新的重構程式碼,發現在Windows下,程式的CPU很高,測試發現select函式並沒有等待,return-1,我們的程式碼原來使用的是rector模式,裡面會用select當作反應器,處理IO事件,在沒有IO事件時當作sleep。但我們的業務伺服器沒有任何要處理的IO控制代碼,所以就相當於呼叫的是

select(0,NULL,NULL,NULL,wait_timeval);

這種方式在Linux下就相當於sleep,而Windows下卻之間返回了-1,認為你傳遞的引數錯誤。查詢了一下MSDN發現有如下說明。

Microsoft MSDN:

Any two of the parameters, readfds, writefds, or exceptfds, can be given as null. At least one must be non-null, and any non-null descriptor set must contain at least one handle to a socket.

 

所以在Windows下,希望將select作為sleep的方法是不可行的,

修正問題的方法也很簡單,我們有一層OS適配層,在其中對於select函式做了一下重新包裝,在3個控制代碼陣列都為NULL的時候,直接呼叫::Sleep,這個就OK了。

值得注意的是ACE在這塊處理上也沒有解決這個問題,也可以算是一個ACE的陷阱。但由於ACE的Rector包裝內部的notify佇列會註冊一個控制代碼到select中(奇怪的是我已經使用了ACE_HAS_REACTOR_NOTIFICATION_QUEUE巨集,這個巨集應該讓notify使用訊息佇列而不是網路通訊方式),所以預設情況下你不會發現這個問題,當你關閉notify佇列時(reactor的open函式引數),問題也一樣出現了。

2          非阻塞連線失敗觸發的差異

放假前最後一天,yunfeiyang突然告訴我說通訊程式作為Windows下沒有進行重連處理,一起定位了一下,發現Windows下需要非阻塞connect其他伺服器連線在連線失敗後就沒有任何事件觸發。

仔細看了看程式碼,我們的程式碼是在connect失敗後,如果返回錯誤是EWOULDBLOCK後,就認為成功發起了非阻塞連線。等待寫事件和讀事件,如果連線成功,如果連線失敗,應該會觸發讀寫事件(我的程式碼優先處理讀事件)。這個和《UNIX網路程式設計:卷1》的描述一致,而且在Linux下測試也正常。但事實是在Windows下,非阻塞連線失敗後,讀寫事件都沒有觸發。

突然想起來,我最早的通訊伺服器是使用ACE作為底層的,當時的測試過沒有這個問題。於是翻出了當時測試的小例子開始除錯。發現連線失敗後,ACE的ACE_Select_Reactor可以觸發handle_close事件。於是有點暈了,再進一步檢查程式碼發現在ACE註冊事件的時候有這樣一段。

複製程式碼

//註冊事件時的程式碼:
// EXCEPT (and CONNECT on Win32) flag will place the handle in
// the except set.
      if (ACE_BIT_ENABLED (mask, ACE_Event_Handler::EXCEPT_MASK)
#if defined (ACE_WIN32)
          || ACE_BIT_ENABLED (mask, ACE_Event_Handler::CONNECT_MASK)
#endif /* ACE_WIN32 */
          )
        {
          (handle_set.ex_mask_.*ptmf) (handle);
        }

複製程式碼

其針對WIN32平臺有特殊處理,馬上去翻了翻MSDN,發現果然,針對異常事件的控制代碼描述如下:果然微軟平臺的處理和Linux不一樣,對於非阻塞連線失敗,觸發的是異常事件。

複製程式碼

Microsoft MSDN:

exceptfds:

    If processing a connect call (nonblocking), connection attempt failed.

    OOB data is available for reading (only if SO_OOBINLINE is disabled).

複製程式碼

 

 

另外糟糕的是,這種問題無法在底層遮蔽(因為底層根本不知道你觸發某個事件的目的是什麼),這種差異只能在上層封裝中解決。

如果有時間會總結一些Linux+Windows跨平臺的文章,估計大標題可以寫成狗日的微