天天看点

【原创】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 了。 

继续阅读