为 Unix 程序员准备的 Windows 异步 I/O 教程

同样的,在Windows系统上也能够找到select这个系统调用。但是,select 在文件描述上实现的是一个O(n)的算法,他并不像现在常用的实时多路复用的 epoll,这也让使 select在高并发服务器上没了用武之地。 接下来,我们将讲述的是Windows下的高并发服务器的设计.

除了epoll或者kqueue, Windows也有自己的多路复用I/O,叫做/O completion ports(IOCPs). IOCPs采用轮寻overlapped I/O方式。并且 IOCP 的消耗时间是一个常数 (REF?).

最根本的变化是,在Unix下,你一般要求内核等待一个文件描述符的可读性或writablity状态变化。使用overlapped I/O和IOCPs,我们将会等待异步方法调用的完成。举例来说,我们不是要等待一个socket的状态成为可写,然后用send(2)就可以了, 就像我们在 Unix 常做的一样, 使用overlapped I/O你应该使用 WSASend()发送消息,并且等待消息发送完全。

Unix非堵塞I/O不是完美的,在Unix系统中的原则抽象是将许多事情的处理统一看做对文件的操作(或者更准确的说是文件描述符)。write(2) read(2) close(2)在TCP协议下工作就像它们对常规文件的操作。同步操作工作在类似的不同文件描述符,但是一旦对性能的需求驱动你去使用O_NONBLOCK变量类型能起到完全不同的作用,甚至对于最基本的操作。尤其,常规系统文件不支持非阻塞操作。(令人不安的是没有人提及这个相当重要的事实)例如, 当它在做非阻塞读操作时,尽管安全的,一个人不可能在一个常规文件FD中轮询是否是可读的。常规文件总是可读的,并且read(2)调用总是有可能阻塞一个调用线程在一个未知的时间里。

POSIX 对于一些操作已经定义了 异步接口,但很多实现这些操作的 Unix,其情形并不明确。在 Linux 上,aio_* 例程使用 pthread,实现于 GNU libc 的用户态。io_submit(2) 没有 GNU libc 的封装,这被报告为非常缓慢,有可能阻塞Solaris 拥有真正的内核 AIO,但不清楚,与磁盘 I/O 相比,Socket I/O 的性能特性是什么。现代高性能 Unix socket 程序通过 I/O 复用器来使用非阻塞的文件描述符,而非 POSIX 的 AIO。异步访问磁盘的通常做法依然是通过使用定制的用户态线程池来完成,而非 POSIX 的AIO。

Windows 的 IOCP 不同时支持 socket 和 普通文件 I/O,后者可以极大的简化磁盘操作。 例如,ReadFileEx() 对两者都支持。第一个例子,我们先来看看 ReadFile() 是如何工作的。

typedef void* HANDLE;

BOOL ReadFile(HANDLE file,
              void* buffer,
              DWORD numberOfBytesToRead,
              DWORD* numberOfBytesRead,
              OVERLAPPED* overlapped);

这个函数执行读取时可以用同步或异步两种方式。同步操作是通过返回0和 WSAGetLastError()返回 WSA_IO_PENDING 来表示的。当 ReadFile() 异步运行时,用户提供的 OVERLAPPED* 是一个不完全操作的句柄。

typedef struct {
  unsigned long* Internal;
  unsigned long* InternalHigh;
  union {
    struct {
      WORD Offset;
      WORD OffsetHigh;
    };
    void* Pointer;
  };
  HANDLE hEvent;
} OVERLAPPED;

要调查这些函数的完成情况,可以使用IOCP,overlapped->hEvent, 和 GetQueuedCompletionStatus()

简单的TCP连接例子

为了展示GetQueuedCompletionStatus()的使用,提出了一个本地连接到端口8000的例子

char* buffer[200];
WSABUF b = { buffer, 200 };
size_t bytes_recvd;
int r, total_events;
OVERLAPPED overlapped;
HANDLE port;

port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);
if (!port) {
  goto error;
}


r = WSARecv(socket, &b, 1, &bytes_recvd, NULL, &overlapped, NULL);

CreateIoCompletionPort(port, &overlapped.hEvent,

if (r == 0) {
  if (WSAGetLastError() == WSA_IO_PENDING) {
    /* Asynchronous */
    GetQueuedCompletionStatus()


    if (r == WAIT_TIMEOUT) {
      printf("Timeout\n");
    } else {
     
    }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/ff9f9f033b8d414e40d27e4268331cb8.html