天天看點

【原創】modb 功能設計之“跨線程通信”

【第一個版本】 

      使用 pipe 作為跨線程通信方式,使用如下代碼來支援 pipe:

<a href="http://my.oschina.net/moooofly/blog/184986#">?</a>

1

2

3

4

5

6

<code># 使用 _pipe 來模拟 pipe</code>

<code>#if defined(win32) || defined(_win32)</code>

<code>#include &lt;io.h&gt;</code>

<code>#include &lt;fcntl.h&gt;</code>

<code>#define pipe(fds) _pipe(fds, 4096, _o_binary)</code>

<code>#endif</code>

      此時運作實作了 sql 線程的 demo,發現 sql 線程總是無法正常運作,在調用 libevent 接口 event_base_dispatch 時會有錯誤 “10038” 報出。最終查出的結論是:

<code>wsaenotsock                    (10038)             socket operation on non-socket.</code>

操作試圖不是在套接字上進行。它可能是套接字句柄參數沒有引用到一個合法套接字,或者是調用 select() 函數時,一個 fd_set 中的成員不合法。

      查來查去,結果發現,就是 windows 下不能将 pipe(其實是 _pipe)和 select 一起使用的原因。因為 windows 平台上會認為 pipe 産生的 fd 不是一個合法的 socket 句柄,在将讀端 fds[0] 添加進 libevent 進行可讀監聽後(在 windows 平台下 libevent 使用 select 進行 fd 的檢測),就會報出上面的 “10038” 錯誤;與此對應的,linux 下認為“一切皆檔案”,是以是沒有這個問題的。這讓我再一次鄙視了 ms windows。 

_pipe() 的實作如下:

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<code>int</code> <code>__cdecl _pipe (</code>

<code>        </code><code>int</code> <code>phandles[2],</code>

<code>        </code><code>unsigned psize,</code>

<code>        </code><code>int</code> <code>textmode</code>

<code>        </code><code>)</code>

<code>{</code>

<code>    </code><code>int</code> <code>handle0, handle1;</code>

<code>    </code><code>handle</code> <code>readhandle, writehandle;</code>

<code>    </code><code>...</code>

<code>    </code><code>if</code> <code>(!createpipe(&amp;readhandle, &amp;writehandle, &amp;securityattributes, psize)) { </code><code>// 調用 win api 建立 pipe</code>

<code>        </code><code>...</code>

<code>        </code><code>return</code> <code>-1;</code>

<code>    </code><code>}</code>

<code>    </code><code>/* now we must allocate c runtime handles for read and write handles */</code>

<code>    </code><code>if</code> <code>((handle0 = _alloc_osfhnd()) != -1) {  </code><code>// 擷取可用的 c 運作時句柄用作 pipe 的讀端</code>

<code>        </code><code>if</code> <code>((handle1 = _alloc_osfhnd()) != -1) { </code><code>// 擷取可用的 c 運作時句柄用作 pipe 的寫端</code>

<code>        </code><code>_set_osfhnd(handle0, (</code><code>intptr_t</code><code>)readhandle); </code><code>// 将 crt 檔案句柄和 win32 handle 進行關聯</code>

<code>        </code><code>_set_osfhnd(handle1, (</code><code>intptr_t</code><code>)writehandle);</code>

<code>        </code><code>}</code>

<code>    </code><code>phandles[0] = handle0;</code>

<code>    </code><code>phandles[1] = handle1;</code>

<code>     </code> 

<code>    </code><code>return</code> <code>0;</code>

<code>}</code>

      沒辦法,因為有 windows 下調試的需要,既然在 windows 下無法對非 socket fd 進行 select,那麼可選方案隻有通過 socket 來實作 pipe 了,備選方案如下:

通過 socket 模拟 pipe 的實作

使用 socketpair 實作(同樣是通過 socket 來模拟 unix 下的 socketpair)

ps:另外一個出現的錯誤情況是,windows 下再調用 select() 前,需要先調用 wsastartup(),否則會報出 “10093” 錯誤。 

<code>wsanotinitialised           (10093)             successful wsastartup() not yet performed.</code>

應用程式沒有調用 wsastartup() 函數,或函數 wsastartup() 調用失敗了。應用程式可能通路了不屬于目前活動任務的套接字(例如試圖在任務間共享套接字),或調用了過多的 wsacleanup() 函數。 

【第二個版本】 

      翻閱大量開源軟體的代碼後,經過對比發現後發現,兩者的實作代碼基本相同,隻是名字不同而已。換句話說,通過 socket 模拟出來的這個東東,既可以替代 pipe(通常用于具有父子關系的程序間通信)的功能,也可以替代 socketpair(通常用于 af_unix 域)的功能。 

      在采用 socket 實作的 pipe 後,首先遇到的問題是,調用 write( sql_event_thread-&gt;notify_send_fd, "", 1 ) 會報如下斷言錯誤: 

<code>vctools\crt_bld\self_x86\crt\src\write.c</code>

<code>line: 67</code>

<code>expression: (fh &gt;= 0 &amp;&amp; (unsigned)fh &lt; (unsigned)_nhandle)</code>

其中 fh 為程式建立的 socket 句柄的值(例如 12116),_nhandle 是系統定義值(發現其值為 32)。 _nhandle 為什麼是 32 ?這個值代表什麼意思?(讀者思考)

【原創】modb 功能設計之“跨線程通信”

      總之,結論表明在 windows 平台下,對 socket fd 直接進行 read/write 會有錯誤産生。 沒辦法還是換成标準的 socket 函數 send/recv 吧。 

結果一切都 ok 了。 

繼續閱讀