异步机制 - IO完成端口
1 KQUEUE KeInitializeQueue
VOID
KeInitializeQueue(
IN PKQUEUE Queue,
IN ULONG Count OPTIONAL
);
lkd> dt _KQUEUE
nt!_KQUEUE
+0x000 Header : _DISPATCHER_HEADER
+0x010 EntryListHead : _LIST_ENTRY
+0x018 CurrentCount : Uint4B
+0x01c MaximumCount : Uint4B
+0x020 ThreadListHead : _LIST_ENTRY
KeInitializeQueue初始化Queue对象,线程可以等待访问队列中的元素,Count 表示同时可以访问队列的线程数(不需要等待),一般使用KQUEUE的时候也会创建对应数量的线程来queue and dequeue its entries
2 KQUEUE KeInsertQueue
LONG
KeInsertQueue(
IN PKQUEUE Queue,
IN PLIST_ENTRY Entry
);
如果当前线程的不是处于queue wait状态,或者等待的QUEUE不是要插入的这个QUEUE,并且有其它线程在等待KQUEUE(调用KeRemoveQueue),活跃的线程小于最大的活跃线程,则会唤醒等待KQUEUE的线程来取本次要插入的Entry,否则把Entry插入到Queue的尾部.
3 CreateIoCompletionPort - ExistingCompletionPort为NULL的情况
HANDLE WINAPI CreateIoCompletionPort(
__in HANDLE FileHandle,
__in HANDLE ExistingCompletionPort,
__in ULONG_PTR CompletionKey,
__in DWORD NumberOfConcurrentThreads
);
内部创建一个完成端口对象,对象开始是一个_KQUEUE对象,然后调用KeInitializeQueue初始化这个KQUEUE,然后返回这个对象的句柄。如果FileHandle不为NULL,则调用NtSetInformationFile把FileHandle和完成端口进行绑定关联
CompletionInfo.Port = ExistingCompletionPort;
CompletionInfo.Key = (void *)CompletionKey;
NtSetInformationFile(FileHandle, &IoSb, &CompletionInfo, 8, FileCompletionInformation);
关于NtSetInformationFile内部的实现,先看FILE_OBJECT对象
lkd> dt _FILE_OBJECT
nt!_FILE_OBJECT
...
+0x06c CompletionContext : Ptr32 _IO_COMPLETION_CONTEXT
lkd> dt _IO_COMPLETION_CONTEXT
nt!_IO_COMPLETION_CONTEXT
+0x000 Port : Ptr32 Void
+0x004 Key : Ptr32 Void
也就是说内部先分配一个IO_COMPLETION_CONTEXT结构体,然后根据CompletionInfo里面的Port句柄获得Port对象,然后填充这个结构体,最后把这个结构体的指针放到FileHandle对应的文件对象的CompletionContext里面。一个CompletionPort可以关联多个FileHandle,可以通过CompletionKey来标识具体的FileHandle
4 PostQueuedCompletionStatus
BOOL WINAPI PostQueuedCompletionStatus(
__in HANDLE CompletionPort,
__in DWORD dwNumberOfBytesTransferred,
__in ULONG_PTR dwCompletionKey,
__in LPOVERLAPPED lpOverlapped //这里只是个指针,不一定非要指向OVERLAPPED结构体
);
往CompletionPort投递一个I/O completion packet,包含dwNumberOfBytesTransferred, dwCompletionKey和lpOverlapped.
PostQueuedCompletionStatus内部调用NtSetIoCompletion来实现具体的功能
NTSTATUS
NtSetIoCompletion (
__in HANDLE IoCompletionHandle, //CompletionPort
__in PVOID KeyContext, //dwCompletionKey
__in_opt PVOID ApcContext, //为0
__in NTSTATUS IoStatus, //lpOverlapped
__in ULONG_PTR IoStatusInformation //dwNumberOfBytesTransferred
)
NtSetIoCompletion内部大概实现如下
IoCompletionHandle获得完成端口对象IoCompletion,分配一个结构体_IOP_MINI_COMPLETION_PACKET MiniPacket,根据NtSetIoCompletion传进的参数初始化MiniPacket,调用KeInsertQueue((PKQUEUE)IoCompletion, &MiniPacket->ListEntry);插入到完成端口对象的KQUEUE里面_IOP_MINI_COMPLETION_PACKETL里面的PacketType表示类型,比如IopCompletionPacketIrp表示是IRP IO完成时投递的,在NtRemoveIoCompletion里面会跟进这个字段来区分数据
5 GetQueuedCompletionStatus
BOOL WINAPI GetQueuedCompletionStatus(
__in HANDLE CompletionPort,
__out LPDWORD lpNumberOfBytes,
__out PULONG_PTR lpCompletionKey,
__out LPOVERLAPPED* lpOverlapped,
__in DWORD dwMilliseconds
);
内部只是简单的调用NtRemoveIoCompletion
NTSTATUS
NtRemoveIoCompletion (
__in HANDLE IoCompletionHandle,
__out PVOID *KeyContext,
__out PVOID *ApcContext,
__out PIO_STATUS_BLOCK IoStatusBlock,
__in_opt PLARGE_INTEGER Timeout
)
如果是IO
ApcContext = Irp->Overlay.AsynchronousParameters.UserApcContext;
KeyContext = (PVOID)Irp->Tail.CompletionKey;
IoStatusBlock = Irp->IoStatus;
6 完成端口跟FileHandle的内部逻辑
当一次IO完成时,会调用IopCompleteRequest,IopCompleteRequest内部有这一处逻辑
if (fileObject && fileObject->CompletionContext) {
port = fileObject->CompletionContext->Port;
key = fileObject->CompletionContext->Key;
}
irp->Tail.CompletionKey = key;
irp->Tail.Overlay.PacketType = IopCompletionPacketIrp;
KeInsertQueue( (PKQUEUE) port,
&irp->Tail.Overlay.ListEntry );
也就是说,每一次IO完成时,都会向完成端口投递相关信息
7 BindIoCompletionCallback
BOOL WINAPI BindIoCompletionCallback(
__in HANDLE FileHandle,
__in LPOVERLAPPED_COMPLETION_ROUTINE Function,
ULONG Flags //没用
);
微软内部帮你封装好了IO 完成端口的调用,线程池,在文件操作完成时会调用Function
BindIoCompletionCallback内部实现
Status = RtlSetIoCompletionCallback(FileHandle,
(PIO_APC_ROUTINE)Function,
Flags);
RtlSetIoCompletionCallback内部只是把Function当做一个CompletionKey,调用 NtSetInformationFile(FileHandle, &IoSb, &CompletionInfo, 8, FileCompletionInformation); 把Function和文件句柄进行绑定,下次有IO完成时,线程池里面的线程调用ZwRemoveIoCompletion就可以获得CompletionKey,然后进行调用
VOID CALLBACK FileIOCompletionRoutine(
[in] DWORD dwErrorCode, //
[in] DWORD dwNumberOfBytesTransfered,
[in] LPOVERLAPPED lpOverlapped //ApcContext = Irp->Overlay.AsynchronousParameters.UserApcContext;
);
那Irp->Overlay.AsynchronousParameters.UserApcContext从哪来的,在NtReadFile, NtWriteFile里面会设置
NTSTATUS
NtReadFile (
__in HANDLE FileHandle,
__in_opt HANDLE Event,
__in_opt PIO_APC_ROUTINE ApcRoutine,
__in_opt PVOID ApcContext,
__out PIO_STATUS_BLOCK IoStatusBlock,
__out_bcount(Length) PVOID Buffer,
__in ULONG Length,
__in_opt PLARGE_INTEGER ByteOffset,
__in_opt PULONG Key
)
irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
对应的就是ReadFile里面的lpOverlapped
BOOL WINAPI ReadFile(
__in HANDLE hFile,
__out LPVOID lpBuffer,
__in DWORD nNumberOfBytesToRead,
__out LPDWORD lpNumberOfBytesRead,
__in LPOVERLAPPED lpOverlapped
);
8 APC和完成端口
如果绑定了完成端口,那APC是不能干活的,在NtReadFile, NtWriteFile里面会这么判断
if (fileObject->CompletionContext && IopApcRoutinePresent( ApcRoutine )) {
ObDereferenceObject( fileObject );
return STATUS_INVALID_PARAMETER;
}
也就是意味着绑定了完成端口以后,你不能使用ReadFileEx传入回调
异步机制 - IO完成端口的更多相关文章
- windows核心编程---第九章 同步设备IO与异步设备IO之同步IO
同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...
- 异步设备IO OVERLAPPED结构(设备内核对象 事件内核对象 可提醒IO)
同步IO是指:线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备驱动程序的通知. 一.异步准备与OVERLAPPED结构 ...
- 《Windows核心编程系列》九谈谈同步设备IO与异步设备IO之同步设备IO
同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...
- IO完成端口
从MSDN中翻译了IO完成端口的文章,不得不说翻译的很烂,英语需要继续提高啊... 在一个多处理器系统上,IO完成端口提供一个非常高效的线程模型来处理多个异步IO请求.当一个进程创建了一个IO完成端口 ...
- 【windows核心编程】IO完成端口(IOCP)复制文件小例前简单说明
1.关于IOCP IOCP即IO完成端口,是一种高伸缩高效率的异步IO方式,一个设备或文件与一个IO完成端口相关联,当文件或设备的异步IO操作完成的时候,去IO完成端口的[完成队列]取一项,根据完成键 ...
- 异步设备IO:OVERLAPPED和IOCompletionPort
异步设备IO:OVERLAPPED和IOCompletionPort 本文内容为<windows核心编程>第10章内容的总结,仅记录一些本人感兴趣的内容. 1:OVERLAPPED &qu ...
- Windows核心编程:第10章 同步设备IO与异步设备IO
Github https://github.com/gongluck/Windows-Core-Program.git //第10章 同步设备IO与异步设备IO.cpp: 定义应用程序的入口点. // ...
- 《Windows核心编程系列》十谈谈同步设备IO与异步设备IO之异步IO
同步设备IO与异步设备IO之异步IO介绍 设备IO与cpu速度甚至是内存访问相比较都是比较慢的,而且更不可预测.虽然如此,通过使用异步设备IO我们仍然能够创造出更高效的程序. 同步IO时,发出IO请求 ...
- 什么是阻塞、非阻塞、同步和异步以及IO模型
首先先看如下几个问题,或者说我们经常会遇到的问题. 阻塞是否等于同步?非阻塞是否等于异步?同步一定是阻塞的么?异步一定是非阻塞的么?要把这四个概念讲明白,先从一顿简餐说起.假设你要做一顿便饭:烧土豆: ...
随机推荐
- centos系统基本操作命令
系统相关命令 查看系统版本: cat /etc/centos-release 系统更新: yum update 用户相关命令 增加用户: useradd [用户名] 设置密码:password ...
- python matplotlib以日期为x轴作图
from datetime import datetime, date, timedelta import matplotlib.pyplot as plt import tushare as ts ...
- C语言对齐、补齐
加快CPU读取数据的速度 aligned(n) 让所作用的结构成员对齐在n字节自然边界上.如果结构中有成员的长度大于n,则按照最大成员的长度来对齐 struct s { char c; int i; ...
- MySQL 查询大于“时间字段”15分钟、1小时、1天的数据
以下代码中times为时间字段,类型为datetime 1.查询大于times十五分钟的数据 //大于号后面都是获取times十五分钟后的时间select*from table where now() ...
- 定义Java类实现字节流转字符流
package com.buaa.comparable; import java.io.BufferedReader;import java.io.File;import java.io.FileIn ...
- idou老师教你学Istio 23 : 如何用 Istio 实现速率限制
使用 Istio 可以很方便地实现速率限制.本文介绍了速率限制的使用场景,使用 memquota\redisquota adapter 实现速率限制的方法,通过配置 rule 实现有条件的速率限制,以 ...
- less注释
less中的注释 1.可以使用css中的注释(/***/) 2.也可以用//注释 //编译时会自动过滤掉 /**我是会被编译的*/ ul{ // padding: 0px; // height: ...
- 大数据之路week05--day01(JDBC 初识之实现一个系统 实现用户选择增删改查 未优化版本)
要求,实现用户选择增删改查. 给出mysql文件,朋友们可以自己运行导入到自己的数据库中: /* Navicat MySQL Data Transfer Source Server : mysql S ...
- 0005SpringBoot中用Junit测试实体类中绑定yml中的值
1.编写SpringBoot的引导类 package springboot_test.springboot_test; import org.springframework.boot.SpringAp ...
- .NET界面开发新体验!DevExpress v19.2.4全新来袭
DevExpress Universal Subscription(又名DevExpress宇宙版或DXperience Universal Suite)是全球使用广泛的.NET用户界面控件套包,De ...