关于IO完成端口的一些思考

在Jeffery Richter先生的Windows核心编程一书中,有大量篇幅对IO完成端口进行讲解,但是由于这个概念的抽象性和只能基于伪代码的实现,造成了很难理解。

要理解IO完成端口,首先要清楚操作系统的调度,我们在操作系统下开发的程序都属于应用程序,驱动具体的设备执行的是驱动程序,而操作系统恰好处于这两层之间,起一个协调的作用。

因为外围的IO设备相对于主CPU或内存,速度普遍较慢,而我们的应用程序通常是直接加载到内存中,CPU在调用执行指令时速度快,为了达到能操作外部IO数据的目的及一些数据对齐的考虑,势必要等待外部数据,当外设处理到足够的内存块数据时,再由CPU调度内存统一执行,避免资源浪费。

为了达到主CPU的处理对等等待外设IO的数据,就需要操作系统在中间起一些协调作用,只有按照某种约定完成了的数据,才去通知回应用程序,让应用程序中的线程处理后续数据,为了达到最佳的缓存处理机制,微软在操作系统层面上就引入了各种机制,包括可提醒IO,重叠IO,IO完成端口等,其实主要目的就是利用操作系统来缓冲应用程序和驱动程序之间的调用不匹配。

而完成端口作为一种高效的处理机制,他就是在操作系统层面的一种高效缓冲处理算法,所以我们在应用中,就知道了,只需要将要回调的地址或处理的栈地址,一起传入那个可复用的结构,当驱动程序执行完成想要的结果时,会触发对应的回调,因为需要驱动层具体执行,所以在IO端口的应用层面来说,他必须能通知到驱动层怎样执行,而在眼前的大多数适合完成端口的调用来说,我们传入的可执行部分,一般是内核对象,包括一些网络通信,硬盘文件读写内核对象等,所以我们想要利用IO完成端口,执行自己的异步IO调用,就需要在驱动层,对应进行函数的处理,也就是驱动层能暴漏出在应用层调用的地址。

如果又没有自己的设备驱动层,却又想利用完成端口的执行调度效果,则可以做模拟,通过PostQueuedCompletionStatus,来模拟一个IO的请求过程,既然是模拟的,就没有外部设备处理器或驱动层来进行数据处理,我们同时需要去模拟外部的处理器,那实际工作还是由主CPU来干了,就是模拟了其调度效果,因此需要分配额外的工作线程来处理,具体的Post信息的处理。

既然都是主CPU执行了上述模拟过程,岂不是多次一举了吗,其实不然,因为GettQueuedCompletionStatus在作为操作系统层面的消息分发处理时,是维护了一个调用队列,CPU会按照先入先出规则,从GettQueuedCompletionStatus上取消息,当我们有多个核时,我们就可以发起多个监听线程,在这取消息,这个模拟处理机制,最大的好处时,他能在很多场合,实现线程间数据的同步,在多线程并行环境下,线程之间的数据同步往往是另开发人员最为头疼的。而在并发和可伸缩的调度场合,因为GettQueuedCompletionStatus的牵制,可以保证某一时刻正在执行的线程不会过高,因为CPU的处理是很智能的,他只会在运行的线程之间进行时间片的切换,同时GettQueuedCompletionStatus会保证并发线程的调用是后入先出,这样就不会去牺牲CPU不停的去维护线程栈了,响应不高的场合CPU可以利用某个线程栈或寄存器,反复去处理数据,达到高效处理的目的,同时减少了CPU和内存之间的数据交换,因为CPU的高速缓存是很珍贵的。

因为最近正在尝试做一套高性能和并发性好的通用线程池,在WIndows下则还是利用IO 完成端口,但是如何去实现IIS下的智能化和启发式的线程分配策略,却一直没想明白,趁此机会,将自己对IOCP的应用感悟,总结一翻。

你可能感兴趣的:(关于IO完成端口的一些思考)