IOCP(I/O Completion Ports)是Windows操作系统提供的一种高效的异步I/O模型,主要用于处理大量并发I/O请求的应用程序。

IOCP通过以下几个关键概念来实现高效的异步I/O:

  1. Completion Port:这是一个内核对象,多个I/O操作可以与其关联。每个完成端口可以关联多个线程,用于处理I/O完成事件。
  2. I/O Request Packet (IRP):当一个异步I/O请求被发出时,操作系统会创建一个IRP来跟踪这个I/O操作。完成端口会在I/O操作完成时接收到一个包含I/O结果的消息。
  3. Worker Threads:在使用IOCP的应用程序中,通常会创建一个线程池。这些工作线程会等待从完成端口获取I/O完成的通知,然后处理相应的I/O操作。
  4. Asynchronous I/O:使用IOCP的应用程序通常会发出异步I/O请求,而不是阻塞的同步I/O请求。这允许应用程序在等待I/O操作完成的同时继续处理其他任务,从而提高了并发性能。


IOCP工作流程

  • 创建完成端口:使用CreateIoCompletionPort创建一个完成端口。
  • 关联文件句柄:使用CreateIoCompletionPort将文件句柄(如套接字或文件)与完成端口关联。
  • 发出异步I/O请求:使用异步I/O函数(如ReadFile、WriteFile)发出I/O请求,并提供OVERLAPPED结构。
  • 等待I/O完成:工作线程使用GetQueuedCompletionStatus等待I/O完成通知。该函数会阻塞,直到一个I/O操作完成或超时。
  • 处理I/O完成:工作线程获取I/O完成通知后,处理完成的I/O操作(如读取数据、写入数据或处理错误)。

同步I/O和异步I/O

同步I/O

同步I/O操作会阻塞调用线程,直到I/O操作完成。换句话说,线程在发出I/O请求后会等待操作完成,然后再继续执行后续的代码。

特点
  • 阻塞:调用线程在I/O操作完成之前会一直等待,无法执行其他任务。
  • 简单:编程模型简单,容易理解和实现。
  • 适用场景:适用于I/O操作相对较少或可以接受阻塞等待的情况。
异步I/O

异步I/O操作不会阻塞调用线程。调用线程在发出I/O请求后可以立即继续执行其他任务,而操作系统会在I/O操作完成时通知应用程序。

特点
  • 非阻塞:调用线程不必等待I/O操作完成,可以并行处理其他任务,提高了并发性能。
  • 复杂:编程模型相对复杂,需要处理I/O完成的通知和结果。
  • 适用场景:适用于高并发、高吞吐量的应用场景,如服务器和网络编程。

windows异步IO接口

1. CreateIoCompletionPort

CreateIoCompletionPort用于创建一个新的完成端口或将一个文件句柄(如套接字或文件)关联到现有的完成端口。

HANDLE CreateIoCompletionPort(
    HANDLE FileHandle,
    HANDLE ExistingCompletionPort,
    ULONG_PTR CompletionKey,
    DWORD NumberOfConcurrentThreads
);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • FileHandle:要关联的文件句柄。如果为INVALID_HANDLE_VALUE,则创建一个新的完成端口。
  • ExistingCompletionPort:现有的完成端口句柄。如果创建新完成端口,此参数应为NULL。
  • CompletionKey:与文件句柄关联的完成键,完成端口通知时将返回此键。
  • NumberOfConcurrentThreads:建议并发线程数。通常设置为系统的CPU核心数。
2. ReadFile 和 WriteFile

ReadFile和WriteFile是常用的异步读写函数。它们的异步操作需要结合OVERLAPPED结构。

BOOL ReadFile(
    HANDLE hFile,
    LPVOID lpBuffer,
    DWORD nNumberOfBytesToRead,
    LPDWORD lpNumberOfBytesRead,
    LPOVERLAPPED lpOverlapped
);

BOOL WriteFile(
    HANDLE hFile,
    LPCVOID lpBuffer,
    DWORD nNumberOfBytesToWrite,
    LPDWORD lpNumberOfBytesWritten,
    LPOVERLAPPED lpOverlapped
);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
3. GetQueuedCompletionStatus

GetQueuedCompletionStatus用于从完成端口获取I/O完成通知。调用此函数的线程将被阻塞,直到一个I/O操作完成或超时。

BOOL GetQueuedCompletionStatus(
    HANDLE CompletionPort,
    LPDWORD lpNumberOfBytesTransferred,
    PULONG_PTR lpCompletionKey,
    LPOVERLAPPED* lpOverlapped,
    DWORD dwMilliseconds
);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • CompletionPort:完成端口句柄。
  • lpNumberOfBytesTransferred:接收传输的字节数。
  • lpCompletionKey:接收完成键。
  • lpOverlapped:接收指向OVERLAPPED结构的指针。
  • dwMilliseconds:等待的超时时间,以毫秒为单位。如果为INFINITE,则无限等待。
4. PostQueuedCompletionStatus

PostQueuedCompletionStatus用于将一个完成包投递到完成端口。这在模拟I/O操作或控制工作线程方面很有用。

BOOL PostQueuedCompletionStatus(
    HANDLE CompletionPort,
    DWORD dwNumberOfBytesTransferred,
    ULONG_PTR dwCompletionKey,
    LPOVERLAPPED lpOverlapped
);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • CompletionPort:完成端口句柄。
  • dwNumberOfBytesTransferred:传输的字节数。
  • dwCompletionKey:完成键。
  • lpOverlapped:指向OVERLAPPED结构的指针。
5. ReadFileEx 和 WriteFileEx

ReadFileEx和WriteFileEx提供了另一种异步I/O接口,它们通过回调函数通知I/O操作的完成。

BOOL ReadFileEx(
    HANDLE hFile,
    LPVOID lpBuffer,
    DWORD nNumberOfBytesToRead,
    LPOVERLAPPED lpOverlapped,
    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

BOOL WriteFileEx(
    HANDLE hFile,
    LPCVOID lpBuffer,
    DWORD nNumberOfBytesToWrite,
    LPOVERLAPPED lpOverlapped,
    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.