2KB项目,专业的源码交易网站 帮助 收藏 每日签到

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

  • 时间:2019-01-23 18:36 编辑:2KB 来源:2KB.COM 阅读:324
  • 扫一扫,手机访问
  • 分享
摘要: 英文原文:Asy
英文原文:Asynchronous I/O in Windows for Unix Programmers

在阅读之前,我认为你已经掌握了Unix系统上的非阻塞的Socket 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
");
    } else {
      
    }


  } else {
    /* Error */
    printf("Error %d
", WSAGetLastError());
  }
} else {
  /* Synchronous */
  printf("read %ld bytes from socket
", bytes_recvd);
}

先前工作

充分的跨越Unix和Window系统之上写代码是非常困难的, 这要求一个人去理解错综复杂的API和不同系统的非文档化细节。目前已有多个项目试图去提供一个抽象层,但是在作者的观念中,没有人能完全满意这种形式。

Marc Lehmann 的  libev 与  libeio.  libev 是 UNIX I/O 多路复用的最小完美抽象。包括了一些有用的工具函数,如 ev_async,它用于异步通告,但主构件是 ev_io,用于通知用户文件描述符的状态。如前所述,一般通常不可能获得普通文件的状态变化 — 并且即使是 write(2) 和 read(2) 调用,也无法保证它们不阻塞。因此,libeio 被开发出来,用于在可管理的线程池中进行各种磁盘相关的系统调用。不幸的是,libev 做为目标的抽象层并不适合 IOCP — libev 的工作严重依赖于文件描述符,没有 socket 的概念。而且,Unix 的用户可能会将 libeio 用于文件 I/O,但移植到 Windows 上是不理想的。在 Windows 上,libev 当前使用的是 select()—每个线程不超过 64 个文件描述符。

libevent.  比 libev 更庞大,包含了 RPC,DNS,和 HTTP 代码。不支持文件 I/O。libev 在 Lehmann 评估 libevent 并加以拒绝之后开发了它 — 他的理由读起来很有趣。  关键性重写 在版本 2 完成,从而支持 Windows IOCP, 但大量的实例研究显示,它还没有正确的工作。

Boost    ASIO. 它基本上满足你在 Windows 和 Unix 下使用 socket 的需要,即,Linux 的 epoll,Macintosh 的 kqueue,Windows 的 IOCP。它不支持文件 I/O。在笔者看来,对于一个并非十分复杂的问题,它过于庞大了(大约 300 个文件,大约 12000 个分号)。

文件类型

几乎每一个你所熟悉socket操作都有一个异步的对应部分。下面的条目试着就Windows异步I/O系统调用与Unix的非阻塞配个对。

TCP Socket套接字

TCP Sockets是到目前为止最重要的保证正确性的流。服务器被期待去处理成千上万个这样同时发生的每一个线程。即使考虑避免类似Unix主义的文件描述符,在Windows下这种异步I/O也是可能的。(Windows有一个打开的文件描述符不能超过2048的硬限制——具体见 _setmaxstdio()。)

? send(2), write(2)
? Windows:  WSASend() WriteFileEx()
? recv(2), read(2)
? Windows: WSARecv() ReadFileEx()
? connect(2)
? Windows: ConnectEx()和非阻塞  connect()  在Unix下有不同的含义。适当的连接一个远程主机方式是调用connect(2),当它返回 EINPROGRESS 时,轮询文件描述符是否可写。这个时候使用

int error;
socklen_t len = sizeof(int);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len);

一个值为0的 error 变量来告诉我们连接成功。(文档在Linux man page的 connect(2) 的EINPROGRESS错误码中。)
? accept(2)
? Windows: AcceptEx()
? sendfile(2)
? Windows: TransmitFile() 在Unix中对应的确切API是sendfile(2),然而它却没有被同意。每一种操作系统都有些许的不同。Allsendfile(2) 的实现(可能出了FreeBSD吧?)是甚至是在非阻塞的socket上实现的阻塞。Marc Lehmann已经写了 一个libeio可移植性版本库

? Linux sendfile(2)  
? FreeBSD sendfile(2)
? Darwin sendfile(2)

? shutdown(2),优雅的关闭, 半双工连接
? 优雅的 Shutdown, Linger 选项组, 和Socket关闭 DisconnectEx()
? close(2)
? closesocket()

? 下面的条目中在Windows异步和Unix非阻塞几乎是一样的。唯一的不同是Unix中的文件描述符是一个整型,在Windows中使用的是SOCKET类型。
? sockaddr
? bind()
? getsockname()

命名管道

例子:

常规文件

在Unix文件系统中文件不能使用非阻塞I/O。有一些操作系统有异步I/O,但是它不是标准的,至少在Linux系统需要GNU libc的pthreads。为了此应用设计在不同的Unix下是方便的, 必须管理一个线程池为分配文件的I/O系统调用。

在window系统中的较好情况是真正重叠的I/O是可用的,当读或者写一个数据流到文件中。

控制台/TTY

它 (是不是常常?) 可能想一个TCP套接字那样轮询一个Unix TTY 文件描述器来获取可读和可写属性—这非常有益和美好. 在windows中这种境地是非常糟糕的,它不仅仅是一个完全不同的API,还要有读取和写入TTY的不重叠版本. 对可读属性进行轮询可以通过等待在另外一个线程中使用RegisterWaitForSingleObject()来完成.

  • read(2)

  • ReadConsole()ReadConsoleInput()不支持重叠的I/O,并且没有重叠的计数部分. 解决这个问题的一种策略是

    RegisterWaitForSingleObject(&tty_wait_handle, tty_handle,
      tty_want_poll, NULL, INFINITE, WT_EXECUTEINWAITTHREAD |
      WT_EXECUTEONLYONCE)

    其将会在一个不同的线程中执行 tty_want_poll() . 你可以使用这个东西去通知被调用的线程ReadConsoleInput() 将不会阻塞.

  • write(2)

  • WriteConsole()也是阻塞的,但这可能是可以被接受的.

  • tcsetattr(3)

  • SetConsoleMode()

什锦链接

小贴士

IOCP:

APC:

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部