【原创】modb 功能设计之“跨线程通讯”


      在《 modb 功能设计之“支持多消费者单生产者” 》中曾提到,须要解决“rabbitmq 线程与 sql 执行线程之间的跨线程通讯手段”。本文针对这个问题进行一些说明。

【第一个版本】
      使用 pipe 做为跨线程通讯方式,使用以下代码来支持 pipe:
# 使用 _pipe 来模拟 pipe 
#if defined(WIN32) || defined(_WIN32) 
#include <io.h> 
#include <fcntl.h> 
#define pipe(fds) _pipe(fds, 4096, _O_BINARY) 
#endif

      此时运行实现了 sql 线程的 demo,发现 sql 线程老是没法正常运行,在调用 libevent 接口 event_base_dispatch 时会有错误 “10038” 报出。最终查出的结论是: linux

WSAENOTSOCK                    (10038)             Socket operation on non-socket.

操做试图不是在套接字上进行。它多是套接字句柄参数没有引用到一个合法套接字,或者是调用 select() 函数时,一个 fd_set 中的成员不合法。 sql

      查来查去,结果发现,就是 windows 下不能将 pipe(实际上是 _pipe)和 select 一块儿使用的缘由。由于 windows 平台上会认为 pipe 产生的 fd 不是一个合法的 socket 句柄,在将读端 fds[0] 添加进 libevent 进行可读监听后(在 windows 平台下 libevent 使用 select 进行 fd 的检测),就会报出上面的 “10038” 错误;与此对应的,linux 下认为“一切皆文件”,因此是没有这个问题的。这让我再一次鄙视了 MS Windows。

_pipe() 的实现以下:
int __cdecl _pipe ( 
        int phandles[2], 
        unsigned psize, 
        int textmode 
        ) 
{ 
    int handle0, handle1; 
    HANDLE ReadHandle, WriteHandle; 
    ... 
    if (!CreatePipe(&ReadHandle, &WriteHandle, &SecurityAttributes, psize)) {  // 调用 win api 建立 pipe 
        ... 
        return -1; 
    } 
    /* now we must allocate C Runtime handles for Read and Write handles */ 
    if ((handle0 = _alloc_osfhnd()) != -1) {   // 获取可用的 C 运行时句柄用做 pipe 的读端 
        ... 
        if ((handle1 = _alloc_osfhnd()) != -1) {  // 获取可用的 C 运行时句柄用做 pipe 的写端 
        ... 
        _set_osfhnd(handle0, (intptr_t)ReadHandle);  // 将 CRT 文件句柄和 Win32 HANDLE 进行关联 
        _set_osfhnd(handle1, (intptr_t)WriteHandle); 
        } 
    } 
    ... 
    phandles[0] = handle0; 
    phandles[1] = handle1; 
     
    return 0; 
}

      没办法,由于有 windows 下调试的须要,既然在 windows 下没法对非 socket fd 进行 select,那么可选方案只有经过 socket 来实现 pipe 了,备选方案以下: shell

  1. 经过 socket 模拟 pipe 的实现
  2. 使用 socketpair 实现(一样是经过 socket 来模拟 unix 下的 socketpair)
PS:另一个出现的错误状况是,windows 下再调用 select() 前,须要先调用 WSAStartup(),不然会报出 “10093” 错误。 
WSANOTINITIALISED           (10093)             Successful WSAStartup() not yet performed.

应用程序没有调用 WSAStartup() 函数,或函数 WSAStartup() 调用失败了。应用程序可能访问了不属于当前活动任务的套接字(例如试图在任务间共享套接字),或调用了过多的 WSACleanup() 函数。  windows


【第二个版本】

      翻阅大量开源软件的代码后,通过 对比发现后发现,二者的实现代码基本相同,只是名字不一样而已。换句话说,经过 socket 模拟出来的这个东东,既能够替代 pipe(一般用于具备父子关系的进程间通讯)的功能,也能够替代 socketpair(一般用于 AF_UNIX 域)的功能。

      虽然上面说过,使用 socket 模拟 pipe 和模拟 socketpair 在代码实现上是没有差异的,但并非说 linux 下的 pipe 和 socketpair 没有差异,因此下面的讨论以实现 pipe 为例。 具体代码能够参考我以前写过的一篇博客:《 MySQL Proxy中socketpair的使用 》。

      在采用 socket 实现的 pipe 后,首先遇到的问题是,调用 write( sql_event_thread->notify_send_fd, "", 1 ) 会报以下断言错误:
vctools\crt_bld\self_x86\crt\src\write.c 
Line: 67 
Expression: (fh >= 0 && (unsigned)fh < (unsigned)_nhandle)

其中 fh 为程序建立的 socket 句柄的值(例如 12116),_nhandle 是系统定义值(发现其值为 32)。 _nhandle 为何是 32 ?这个值表明什么意思?(读者思考)

api

      总之,结论代表在 windows 平台下,对 socket fd 直接进行 read/write 会有错误产生。 没办法仍是换成标准的 socket 函数 send/recv 吧。

结果一切都 ok 了。


=== 未完待续 ===
相关文章
相关标签/搜索