原文地址:http://support.microsoft.com/kb/192800
翻译地址:http://www.libing.net.cn/post/Design-Issues-When-Using-IOCP-in-a-Winsock-Server.php
欢迎转载,请保留翻译者。
译者:胡章优 huzhangyou
MSN: huzhangyou2002(at)gmail.com
QQ:3803308
Blog:http://www.libing.net.cn
SUMMARY
This article assumes you already understand the I/O model of the Windows NT I/O Completion Port (IOCP) and are familiar with the related APIs. If you want to learn IOCP, please see Advanced Windows (3rd edition) by Jeffery Richter, chapter 15 Device I/O for an excellent discussion on IOCP implementation and the APIs you need to use it.
译文:阅读本文以前已经假设您已经理解windows NT系统的 I/O完成端口 的I/O模型,同时熟悉相关的API函数。如果你想学习IOCP,请阅读Jeffery Richter的Advanced Windows (3rd edition)第15章。里面包含了相关的API函数介绍以及讨论了IOCP的实现。
An IOCP provides a model for developing very high performance and very scalable server programs. Direct IOCP support was added to Winsock2 and is fully implemented on the Windows NT platform. However, IOCP is the hardest to understand and implement among all Windows NT I/O models. To help you design a better socket server using IOCP, a number of tips are provided in this article.
译文:一个IOCP提供了一个开发非常高效性能以及可扩展的 服务器程序模型。IOCP模型是Winsock2加进来的,同时在Windows NT平台下面实现。尽管如此,IOCP是最难理解同时在所有的Windows NT的I/O模型中最难实现的。为了帮助你理解如何设计一个IOCP服务器,下面的一些关键点可能会给你带来一些帮助。
MORE INFORMATION
TIP 1: Use Winsock2 IOCP-capable functions, such as WSASend and WSARecv, over Win32 file I/O functions, such as WriteFile and ReadFile.
译文:
提示1:使用Winsock2的IOCP函数比如WSASend ,WSARecv替代Win32 文件I/O函数,如WriteFile ,ReadFile。
Socket handles from Microsoft-based protocol providers are IFS handles so you can use Win32 file I/O calls with the handle. However, the interactions between the provider and file system involve many kernel/user mode transition, thread context switches, and parameter marshals that result in a significant performance penalty. You should use only Winsock2 IOCP- capable functions with IOCP.
译文:
如果您使用Win32文件I/O函数调用时候,会牵涉倒很多性能损耗的情况。比如内核/用户模态转换,线程切换,参数排列配置。所以推荐使用Winsock2的IOCP的函数。
The additional parameter marshals and mode transitions in ReadFile and WriteFile only occur if the provider does not have XP1_IFS_HANDLES bit set in dwServiceFlags1 of its WSAPROTOCOL_INFO structure.
译文:
ReadFile 和WriteFile 的附加参数排列配置和模态切换发生在dwServiceFlags1 结构中的WSAPROTOCOL_INFO的XP1_IFS_HANDLES的位操作没有设置时候。
NOTE: These providers have an unavoidable additional mode transition, even in the case of WSASend and WSARecv, although ReadFile and WriteFile will have more of them.
TIP 2: Choose the number of the concurrent worker threads allowed and the total number of the worker threads to spawn.
译文:
提示二:选择相应数量的并发工作线程数量。
The number of worker threads and the number of concurrent threads that the IOCP uses are not the same thing. You can decide to have a maximum of 2 concurrent threads used by the IOCP and a pool of 10 worker threads. You have a pool of worker threads greater than or equal to the number of concurrent threads used by the IOCP so that a worker thread handling a dequeued completion packet can call one of the Win32 "wait" functions without delaying the handling of other queued I/O packets.
译文:
IOCP使用的工作线程的数量和并发线程的数量并不是一回事。你可以使用最大2条并发线程同时10条 工作线程。你可以有很多工作线程,并发线程的数量一般是小于或者等于工作线程数量。工作线程处理一个包含完成数据包状态的队列,如果没有数据,他们将在等待状态。
If there are completion packets waiting to be dequeued, the system will wake up another worker thread. Eventually, the first thread satisfies it's Wait and it can be run again. When this happens, the number of the threads that can be run is higher than the concurrency allowed on the IOCP (for example, NumberOfConcurrentThreads). However, when next worker thread calls GetQueueCompletionStatus and enters wait status, the system does not wake it up. In other words, the system tries to keep your requested number of concurrent worker threads.
译文:
如果有完成的数据包需要出队列,系统将会唤醒另外的线程。最终,第一个线程停止等待并继续运行。当这个触发时,可以执行的线程数量比并发的要高。尽管如 此,当工作线程调用GetQueueCompletionStatus并且进入wait状态,系统并不唤醒它。换句话说,系统尽量保持你要求的并发工作线程数量。( 这里翻译的可能不是很通顺,抱歉)
Typically, you only need one concurrent worker thread per CPU for IOCP. To do this, enter 0 for NumberOfConcurrentThreads in the CreateIoCompletionPort call when you first create the IOCP.
译文:典型的来说,对于每个CPU,你只需要一个并发线程.所以,在调用CreateIoCompletionPort 函数的时候,NumberOfConcurrentThreads 的参数设置为0.
TIP 3: Associate a posted I/O operation with a dequeued completion packet.
GetQueuedCompletionStatus returns a completion key and an overlapped structure for the I/O when dequeuing a completion packet. You should use these two structures to return per handle and per I/O operation information, respectively. You can use your socket handle as the completion key when you register the socket with the IOCP to provide per handle information. To provide per I/O operation "extend" the overlapped structure to contain your application-specific I/O-state information. Also, make sure you provide a unique overlapped structure for each overlapped I/O. When an I/O completes, the same pointer to the overlapped I/O structure is returned.
译文:
提示三:同一个完成队列关联一个I/O操作。
当 dequeuing a completion packet的时候,GetQueuedCompletionStatus 函数返回一个completion key和一个overlapped结构。你应当使用这两个结构去返回每个句柄以及没有I/O操作的信息。当你注册了你的Socket并管理了IOCP以后,你可以使用你的Socket句柄 作为completion key。为了为每个I/O操作提供扩展,Overlapped结构可以包含你程序特定的I/O状态信息。同时,必须保证你为每一个Overlapped I/O提供一个唯一的Overlapped结构.当一个I/O操作完成,同样的指向Overlapped I/O操作的指针将会返回。
TIP 4: I/O completion packet queuing behavior.
The order in which I/O completion packets are queued in the IOCP is not necessarily the same order the Winsock2 I/O calls were made. Additionally, if a Winsock2 I/O call returns SUCCESS or IO_PENDING, it is guaranteed that a completion packet will be queued to the IOCP when the I/O completes, regardless of whether the socket handle is closed. After you close a socket handle, future calls to WSASend, WSASendTo, WSARecv, or WSARecvFrom will fail with a return code other than SUCCESS or IO_PENDING, which will not generate a completion packet. The status of the completion packet retrieved by GetQueuedCompletionStatus for I/O previously posted could indicate a failure in this case.
If you delete the IOCP itself, no more I/O can be posted to the IOCP because the IOCP handle itself is invalid. However, the system's underlying IOCP kernel structures do not go away until all successfully posted I/Os are completed.
译文:
提示四:I/O完成包队列状态
IOCP中哪个I/O完成包在等待的顺序不是必须同winsock2 I/O调用。如果一个winsock2 I/O调用返回SUCCESS或者IO_PENDING。当你关闭一个Socket句柄以后,调用WSASend, WSASendTo, WSARecv, or WSARecvFrom会返回SUCCESS以及IO_PENDING以外的结果,他们不会产生完成包。GetQueuedCompletionStatus函数返回的状态包会出现一个错误。
TIP 5: IOCP cleanup.
The most important thing to remember when performing ICOP cleanup is the same when using overlapped I/O: do not free an overlapped structure if the I/O for it has not yet completed. The HasOverlappedIoCompleted macro allows you to detect if an I/O has completed from its overlapped structure.
译文:
提示5:IOCP的清理。
不要对没有完成的完成端口执行清理操作。HasOverlappedIoCompleted 宏可以帮你判断是否完成端口已经完成。
There are typically two scenarios for shutting down a server. In the first scenario, you do not care about the completion status of outstanding I/Os and you just want to shut down as fast as you can. In the second scenario, you want to shut down the server, but you do need to know the completion status of each outstanding I/O.
译文:
有两种方法去关闭这个Server.在第一种方法中,你不需要关心完成状态。
第二种方法中,你需要关闭server,同时你需要知道完成状态。
In the first scenario, you can call PostQueueCompletionStatus (N times, where N is the number of worker threads) to post a special completion packet that informs the worker thread to exit immediately, close all socket handles and their associated overlapped structures, and then close the completion port. Again, make sure you use HasOverlappedIoCompleted to check the completion status of an overlapped structure before you free it. If a socket is closed, all outstanding I/O on the socket eventually complete quickly.
译文:
第一种方法,发送PostQueueCompletionStatus ,数量为工作线程的数量。
In the second scenario, you can delay exiting worker threads so that all completion packets can be properly dequeued. You can start by closing all socket handles and the IOCP. However, you need to maintain a count of the number of outstanding I/Os so that your worker thread can know when it is safe to exit the thread. The performance penalty of having a global I/O counter protected with a critical section for an IOCP server is not as bad as might be expected because the active worker thread does not switch out if there are more completion packets waiting in the queue.
第二种方法:你可以延迟退出工作线程,因此所有的完成包可以合适的出对。你可以关闭所有的socket句柄和IOCP.因此,你需要维护一系列的活动的I/Os,因此你的线程可以知道那些线程是可以安全退出的。
翻译地址:http://www.libing.net.cn/post/Design-Issues-When-Using-IOCP-in-a-Winsock-Server.php
欢迎转载,请保留翻译者。
译者:胡章优 huzhangyou
MSN: huzhangyou2002(at)gmail.com
QQ:3803308
Blog:http://www.libing.net.cn
SUMMARY
This article assumes you already understand the I/O model of the Windows NT I/O Completion Port (IOCP) and are familiar with the related APIs. If you want to learn IOCP, please see Advanced Windows (3rd edition) by Jeffery Richter, chapter 15 Device I/O for an excellent discussion on IOCP implementation and the APIs you need to use it.
译文:阅读本文以前已经假设您已经理解windows NT系统的 I/O完成端口 的I/O模型,同时熟悉相关的API函数。如果你想学习IOCP,请阅读Jeffery Richter的Advanced Windows (3rd edition)第15章。里面包含了相关的API函数介绍以及讨论了IOCP的实现。
An IOCP provides a model for developing very high performance and very scalable server programs. Direct IOCP support was added to Winsock2 and is fully implemented on the Windows NT platform. However, IOCP is the hardest to understand and implement among all Windows NT I/O models. To help you design a better socket server using IOCP, a number of tips are provided in this article.
译文:一个IOCP提供了一个开发非常高效性能以及可扩展的 服务器程序模型。IOCP模型是Winsock2加进来的,同时在Windows NT平台下面实现。尽管如此,IOCP是最难理解同时在所有的Windows NT的I/O模型中最难实现的。为了帮助你理解如何设计一个IOCP服务器,下面的一些关键点可能会给你带来一些帮助。
MORE INFORMATION
TIP 1: Use Winsock2 IOCP-capable functions, such as WSASend and WSARecv, over Win32 file I/O functions, such as WriteFile and ReadFile.
译文:
提示1:使用Winsock2的IOCP函数比如WSASend ,WSARecv替代Win32 文件I/O函数,如WriteFile ,ReadFile。
Socket handles from Microsoft-based protocol providers are IFS handles so you can use Win32 file I/O calls with the handle. However, the interactions between the provider and file system involve many kernel/user mode transition, thread context switches, and parameter marshals that result in a significant performance penalty. You should use only Winsock2 IOCP- capable functions with IOCP.
译文:
如果您使用Win32文件I/O函数调用时候,会牵涉倒很多性能损耗的情况。比如内核/用户模态转换,线程切换,参数排列配置。所以推荐使用Winsock2的IOCP的函数。
The additional parameter marshals and mode transitions in ReadFile and WriteFile only occur if the provider does not have XP1_IFS_HANDLES bit set in dwServiceFlags1 of its WSAPROTOCOL_INFO structure.
译文:
ReadFile 和WriteFile 的附加参数排列配置和模态切换发生在dwServiceFlags1 结构中的WSAPROTOCOL_INFO的XP1_IFS_HANDLES的位操作没有设置时候。
NOTE: These providers have an unavoidable additional mode transition, even in the case of WSASend and WSARecv, although ReadFile and WriteFile will have more of them.
TIP 2: Choose the number of the concurrent worker threads allowed and the total number of the worker threads to spawn.
译文:
提示二:选择相应数量的并发工作线程数量。
The number of worker threads and the number of concurrent threads that the IOCP uses are not the same thing. You can decide to have a maximum of 2 concurrent threads used by the IOCP and a pool of 10 worker threads. You have a pool of worker threads greater than or equal to the number of concurrent threads used by the IOCP so that a worker thread handling a dequeued completion packet can call one of the Win32 "wait" functions without delaying the handling of other queued I/O packets.
译文:
IOCP使用的工作线程的数量和并发线程的数量并不是一回事。你可以使用最大2条并发线程同时10条 工作线程。你可以有很多工作线程,并发线程的数量一般是小于或者等于工作线程数量。工作线程处理一个包含完成数据包状态的队列,如果没有数据,他们将在等待状态。
If there are completion packets waiting to be dequeued, the system will wake up another worker thread. Eventually, the first thread satisfies it's Wait and it can be run again. When this happens, the number of the threads that can be run is higher than the concurrency allowed on the IOCP (for example, NumberOfConcurrentThreads). However, when next worker thread calls GetQueueCompletionStatus and enters wait status, the system does not wake it up. In other words, the system tries to keep your requested number of concurrent worker threads.
译文:
如果有完成的数据包需要出队列,系统将会唤醒另外的线程。最终,第一个线程停止等待并继续运行。当这个触发时,可以执行的线程数量比并发的要高。尽管如 此,当工作线程调用GetQueueCompletionStatus并且进入wait状态,系统并不唤醒它。换句话说,系统尽量保持你要求的并发工作线程数量。( 这里翻译的可能不是很通顺,抱歉)
Typically, you only need one concurrent worker thread per CPU for IOCP. To do this, enter 0 for NumberOfConcurrentThreads in the CreateIoCompletionPort call when you first create the IOCP.
译文:典型的来说,对于每个CPU,你只需要一个并发线程.所以,在调用CreateIoCompletionPort 函数的时候,NumberOfConcurrentThreads 的参数设置为0.
TIP 3: Associate a posted I/O operation with a dequeued completion packet.
GetQueuedCompletionStatus returns a completion key and an overlapped structure for the I/O when dequeuing a completion packet. You should use these two structures to return per handle and per I/O operation information, respectively. You can use your socket handle as the completion key when you register the socket with the IOCP to provide per handle information. To provide per I/O operation "extend" the overlapped structure to contain your application-specific I/O-state information. Also, make sure you provide a unique overlapped structure for each overlapped I/O. When an I/O completes, the same pointer to the overlapped I/O structure is returned.
译文:
提示三:同一个完成队列关联一个I/O操作。
当 dequeuing a completion packet的时候,GetQueuedCompletionStatus 函数返回一个completion key和一个overlapped结构。你应当使用这两个结构去返回每个句柄以及没有I/O操作的信息。当你注册了你的Socket并管理了IOCP以后,你可以使用你的Socket句柄 作为completion key。为了为每个I/O操作提供扩展,Overlapped结构可以包含你程序特定的I/O状态信息。同时,必须保证你为每一个Overlapped I/O提供一个唯一的Overlapped结构.当一个I/O操作完成,同样的指向Overlapped I/O操作的指针将会返回。
TIP 4: I/O completion packet queuing behavior.
The order in which I/O completion packets are queued in the IOCP is not necessarily the same order the Winsock2 I/O calls were made. Additionally, if a Winsock2 I/O call returns SUCCESS or IO_PENDING, it is guaranteed that a completion packet will be queued to the IOCP when the I/O completes, regardless of whether the socket handle is closed. After you close a socket handle, future calls to WSASend, WSASendTo, WSARecv, or WSARecvFrom will fail with a return code other than SUCCESS or IO_PENDING, which will not generate a completion packet. The status of the completion packet retrieved by GetQueuedCompletionStatus for I/O previously posted could indicate a failure in this case.
If you delete the IOCP itself, no more I/O can be posted to the IOCP because the IOCP handle itself is invalid. However, the system's underlying IOCP kernel structures do not go away until all successfully posted I/Os are completed.
译文:
提示四:I/O完成包队列状态
IOCP中哪个I/O完成包在等待的顺序不是必须同winsock2 I/O调用。如果一个winsock2 I/O调用返回SUCCESS或者IO_PENDING。当你关闭一个Socket句柄以后,调用WSASend, WSASendTo, WSARecv, or WSARecvFrom会返回SUCCESS以及IO_PENDING以外的结果,他们不会产生完成包。GetQueuedCompletionStatus函数返回的状态包会出现一个错误。
TIP 5: IOCP cleanup.
The most important thing to remember when performing ICOP cleanup is the same when using overlapped I/O: do not free an overlapped structure if the I/O for it has not yet completed. The HasOverlappedIoCompleted macro allows you to detect if an I/O has completed from its overlapped structure.
译文:
提示5:IOCP的清理。
不要对没有完成的完成端口执行清理操作。HasOverlappedIoCompleted 宏可以帮你判断是否完成端口已经完成。
There are typically two scenarios for shutting down a server. In the first scenario, you do not care about the completion status of outstanding I/Os and you just want to shut down as fast as you can. In the second scenario, you want to shut down the server, but you do need to know the completion status of each outstanding I/O.
译文:
有两种方法去关闭这个Server.在第一种方法中,你不需要关心完成状态。
第二种方法中,你需要关闭server,同时你需要知道完成状态。
In the first scenario, you can call PostQueueCompletionStatus (N times, where N is the number of worker threads) to post a special completion packet that informs the worker thread to exit immediately, close all socket handles and their associated overlapped structures, and then close the completion port. Again, make sure you use HasOverlappedIoCompleted to check the completion status of an overlapped structure before you free it. If a socket is closed, all outstanding I/O on the socket eventually complete quickly.
译文:
第一种方法,发送PostQueueCompletionStatus ,数量为工作线程的数量。
In the second scenario, you can delay exiting worker threads so that all completion packets can be properly dequeued. You can start by closing all socket handles and the IOCP. However, you need to maintain a count of the number of outstanding I/Os so that your worker thread can know when it is safe to exit the thread. The performance penalty of having a global I/O counter protected with a critical section for an IOCP server is not as bad as might be expected because the active worker thread does not switch out if there are more completion packets waiting in the queue.
第二种方法:你可以延迟退出工作线程,因此所有的完成包可以合适的出对。你可以关闭所有的socket句柄和IOCP.因此,你需要维护一系列的活动的I/Os,因此你的线程可以知道那些线程是可以安全退出的。